360前端星计划—前端动画可以这么玩(月影)

编程入门 行业动态 更新时间:2024-10-08 06:28:20

360前端星计划—前端动画可以这么玩(<a href=https://www.elefans.com/category/jswz/34/1765690.html style=月影)"/>

360前端星计划—前端动画可以这么玩(月影)

JS动画原理与实现

      • 1. 动画的基本原理
      • 2. 动画的种类
        • 2.1 简单动画第一个版本:
        • 2.2 第二个版本:
        • 2.3 通用化
        • 2.3 通用化2
        • 2.3 通用化3
        • 2.4 封装Timing
      • 3. 实例
        • 3.1 匀速运动
        • 3.2 自由落体运动
        • 3.3 摩擦力(匀减速)
        • 3.4 平抛(x轴匀速和y轴匀加速)
        • 3.5 旋转+平抛
        • 3.5 贝塞尔轨迹
        • 3.6 贝塞尔作easing
        • 3.7 bezier-easing 轨迹
        • 3.8 椭圆轨迹
        • 3.9 椭圆周期运动
        • 3.10 连续运动
        • 3.11 线性插值(lerp)
        • 3.12 弹跳的小球
        • 3.13 弹跳的小球2
        • 3.13 滚动
        • 3.14 平稳变速
        • 3.15 甩球
        • 3.16 逐帧动画
      • 4. Web Animation API
      • 5. 总结

1. 动画的基本原理

  • 定时器改变对象的属性
  • 根据新的属性重新渲染动画
function update(context) {// 更新属性
}
const ticker = new Ticker();
ticker.tick(update, context);

2. 动画的种类

  • JavaScript 动画
    - 操作DOM
    - Canvas
  • CSS 动画
    - transition
    - animation
  • SVG 动画
    - SMIL

JavaScript 动画优缺点:

  • 优点:灵活度、可控性、性能
  • 缺点:易用性
2.1 简单动画第一个版本:
let rotation = 0;
requestAnimationFrame(function update() {block.style.transform = `rotate(${rotation++}deg)`; //增量,很难精确知道旋转周期requestAnimationFrame(update);
});
2.2 第二个版本:
let rotation = 0;
let startTime = null;
const T = 2000; //2s周期
requestAnimationFrame(function update() {if(!startTime) startTime = Date.now();const p = (Date.now() - startTime)/T;block.style.transform = `rotate(${360 * p}deg)`; requestAnimationFrame(update);
});
2.3 通用化
function update({target}, count) {target.style.transform = `rotate(${count++}deg)`;
}class Ticker {tick(update, context) {let count = 0;requestAnimationFrame(function next() {if(update(context, ++count) !== false) {requestAnimationFrame(next);}});}
}const ticker = new Ticker();
ticker.tick(update, {target: block});
2.3 通用化2
function update({target}, {time}) {target.style.transform = `rotate(${360 * time / 2000}deg)`;
}class Ticker {tick(update, context) {let count = 0;let startTime = Date.now();requestAnimationFrame(function next() {count++;const time = Date.now() - startTime;if(update(context, {count, time}) !== false) {requestAnimationFrame(next);}});}
}const ticker = new Ticker();
ticker.tick(update, {target: block});
2.3 通用化3
function update({context}, {time}) {context.clearRect(0, 0, 512, 512);context.save();context.translate(100, 100);context.rotate(time * 0.005);context.fillStyle = '#00f';context.fillRect(-50, -50, 100, 100);context.restore();
}class Ticker {tick(update, context) {let count = 0;let startTime = Date.now();requestAnimationFrame(function next() {count++;const time = Date.now() - startTime;if(update(context, {count, time}) !== false) {requestAnimationFrame(next);}});}
}
2.4 封装Timing
class Timing {constructor({duration, easing} = {}) {this.startTime = Date.now();this.duration = duration;this.easing = easing || function(p){return p};}get time() {return Date.now() - this.startTime;}get p() {return this.easing(Math.min(this.time / this.duration, 1.0));}
}class Ticker {tick(update, context, timing) {let count = 0;timing = new Timing(timing);requestAnimationFrame(function next() {count++;if(update(context, {count, timing}) !== false) {requestAnimationFrame(next);}});}}

3. 实例

3.1 匀速运动

function update({target}, {timing}) {target.style.transform = `translate(${200 * timing.p}px, 0)`;
}const ticker = new Ticker();
ticker.tick(update, {target: block}, {duration: 2000}
);
3.2 自由落体运动

function update({target}, {timing}) {target.style.transform = `translate(0, ${200 * timing.p}px)`;
}const ticker = new Ticker();
ticker.tick(update, {target: block}, {duration: 2000,easing: p => p ** 2, //平方映射
});
3.3 摩擦力(匀减速)

function update({target}, {timing}) {target.style.transform = `translate(${200 * timing.p}px, 0)`;
}const ticker = new Ticker();
ticker.tick(update, {target: block}, {duration: 2000,easing: p => p * (2 - p), 
});
3.4 平抛(x轴匀速和y轴匀加速)
class Timing {constructor({duration, easing} = {}) {this.startTime = Date.now();this.duration = duration;this.easing = easing || function(p){return p};}get time() {return Date.now() - this.startTime;}get op() {return Math.min(this.time / this.duration, 1.0);}get p() {return this.easing(this.op);}
}function update({target}, {timing}) {target.style.transform = `translate(${200 * timing.op}px, ${200 * timing.p}px)`;
}
3.5 旋转+平抛
function update({target}, {timing}) {target.style.transform = `translate(${200 * timing.op}px, ${200 * timing.p}px)rotate(${720 * timing.op}deg)`;
}
3.5 贝塞尔轨迹

function bezierPath(x1, y1, x2, y2, p) {const x = 3 * x1 * p * (1 - p) ** 2 + 3 * x2 * p ** 2 * (1 - p) + p ** 3;const y = 3 * y1 * p * (1 - p) ** 2 + 3 * y2 * p ** 2 * (1 - p) + p ** 3;return [x, y];
}function update({target}, {timing}) {const [px, py] = bezierPath(0.2, 0.6, 0.8, 0.2, timing.p);target.style.transform = `translate(${100 * px}px, ${100 * py}px)`;
}const ticker = new Ticker();
ticker.tick(update, {target: block}, {duration: 2000,easing: p => p * (2 - p),
});
3.6 贝塞尔作easing
  • B(px) 作为输入, B(py) 作为输出
  • 通过牛顿迭代,从B(px)求p,从p求B(py)
function update({target}, {timing}) {target.style.transform = `translate(${100 * timing.p}px, 0)`;
}const ticker = new Ticker();
ticker.tick(update, {target: block}, {duration: 2000,easing: BezierEasing(0.5, -1.5, 0.5, 2.5),
});
3.7 bezier-easing 轨迹
function update({target}, {timing}) {target.style.transform =`translate(${100 * timing.p}px, ${100 * timing.op}px)`;
}const ticker = new Ticker();
ticker.tick(update, {target: block}, {duration: 2000,easing: BezierEasing(0.5, -1.5, 0.5, 2.5),
});
3.8 椭圆轨迹

class Timing {constructor({duration, easing, iterations = 1} = {}) {this.startTime = Date.now();this.duration = duration;this.easing = easing || function(p){return p};this.iterations = iterations;}get time() {return Date.now() - this.startTime;}get finished() {return this.time / this.duration >= 1.0 * this.iterations;}get op() {let op = Math.min(this.time / this.duration, 1.0 * this.iterations);if(op < 1.0) return op;op -= Math.floor(op);return op > 0 ? op : 1.0;}get p() {return this.easing(this.op);}
}
3.9 椭圆周期运动
function update({target}, {timing}) {const x = 150 * Math.cos(Math.PI * 2 * timing.p);const y = 100 * Math.sin(Math.PI * 2 * timing.p);target.style.transform = `translate(${x}px, ${y}px)`;
}const ticker = new Ticker();
ticker.tick(update, {target: block},{duration: 2000, iterations: 10});
3.10 连续运动
class Ticker {tick(update, context, timing) {let count = 0;timing = new Timing(timing);return new Promise((resolve) => {requestAnimationFrame(function next() {count++;if(update(context, {count, timing}) !== false && !timing.finished) {requestAnimationFrame(next);} else {resolve(timing);}});      });}
}
function left({target}, {timing}) {target.style.left = `${100 + 200 * timing.p}px`;
}
function down({target}, {timing}) {target.style.top = `${100 + 200 * timing.p}px`;
}
function right({target}, {timing}) {target.style.left = `${300 - 200 * timing.p}px`;
}
function up({target}, {timing}) {target.style.top = `${300 - 200 * timing.p}px`;
}(async function() {const ticker = new Ticker();await ticker.tick(left, {target: block},{duration: 2000});await ticker.tick(down, {target: block},{duration: 2000});await ticker.tick(right, {target: block},{duration: 2000});await ticker.tick(up, {target: block},{duration: 2000});
})();
3.11 线性插值(lerp)

f§ = from + (to - from) * p
f§ = to * p + from * (1 - p)

function lerp(setter, from, to) {return function({target}, {timing}) {const p = timing.p;const value = {};for(let key in to) {value[key] = to[key] * p + from[key] * (1 - p);}setter(target, value);}
}
function setValue(target, value) {for(let key in value) {target.style[key] = `${value[key]}px`;}
}const left = lerp(setValue, {left: 100}, {left: 300});
const down = lerp(setValue, {top: 100}, {top: 300});
const right = lerp(setValue, {left: 300}, {left: 100});
const up = lerp(setValue, {top: 300}, {top: 100});(async function() {const ticker = new Ticker();await ticker.tick(left, {target: block},{duration: 2000});await ticker.tick(down, {target: block},{duration: 2000});await ticker.tick(right, {target: block},{duration: 2000});await ticker.tick(up, {target: block},{duration: 2000});
})();
3.12 弹跳的小球
const down = lerp(setValue, {top: 100}, {top: 300});
const up = lerp(setValue, {top: 300}, {top: 100});(async function() {const ticker = new Ticker();// noprotectwhile(1) {await ticker.tick(down, {target: block},{duration: 2000, easing: p => p * p});await ticker.tick(up, {target: block},{duration: 2000, easing: p => p * (2 - p)});}
})();
3.13 弹跳的小球2

(async function() {
const ticker = new Ticker();
let damping = 0.7,
duration = 2000,
height = 300;

// noprotect
while(height >= 1) {
let down = lerp(setValue, {top: 400 - height}, {top: 400});
await ticker.tick(down, {target: block},
{duration, easing: p => p * p});
height *= damping ** 2;
duration *= damping;
let up = lerp(setValue, {top: 400}, {top: 400 - height});
await ticker.tick(up, {target: block},
{duration, easing: p => p * (2 - p)});
}
})();

3.13 滚动
const roll = lerp((target, {left, rotate}) => {target.style.left = `${left}px`;target.style.transform = `rotate(${rotate}deg)`;},  {left: 100, rotate: 0}, {left: 414, rotate: 720});const ticker = new Ticker();ticker.tick(roll, {target: block},{duration: 2000, easing: p => p});
3.14 平稳变速
function forward(target, {y}) {target.style.top = `${y}px`;
}(async function() {const ticker = new Ticker();await ticker.tick(lerp(forward, {y: 100}, {y: 200}), {target: block},{duration: 2000, easing: p => p * p}); await ticker.tick(lerp(forward, {y: 200}, {y: 300}), {target: block},{duration: 1000, easing: p => p}); await ticker.tick(lerp(forward, {y: 300}, {y: 350}), {target: block},{duration: 1000, easing: p => p * (2 - p)}); 
}());
3.15 甩球
function circle({target}, {timing}) {const p = timing.p;const rad = Math.PI * 2 * p;const x = 200 + 100 * Math.cos(rad);const y = 200 + 100 * Math.sin(rad);target.style.left = `${x}px`;target.style.top = `${y}px`;
}
function shoot({target}, {timing}) {const p = timing.p;const rad = Math.PI * 0.2;const startX = 200 + 100 * Math.cos(rad);const startY = 200 + 100 * Math.sin(rad);const vX = -100 * Math.PI * 2 * Math.sin(rad);const vY = 100 * Math.PI * 2 * Math.cos(rad);const x = startX + vX * p;const y = startY + vY * p;target.style.left = `${x}px`;target.style.top = `${y}px`;
}
(async function() {const ticker = new Ticker();await ticker.tick(circle, {target: block},{duration: 2000, easing: p => p, iterations: 2.1}); await ticker.tick(shoot, {target: block},{duration: 2000});
}());
3.16 逐帧动画
<style type="text/css">
.sprite {display:inline-block; overflow:hidden; background-repeat: no-repeat;background-image:url(.png);
}.bird0 {width:86px; height:60px; background-position: -178px -2px}
.bird1 {width:86px; height:60px; background-position: -90px -2px}
.bird2 {width:86px; height:60px; background-position: -2px -2px}#bird{position: absolute;left: 100px;top: 100px;zoom: 0.5;}
</style>
<div id="bird" class="sprite bird1"></div>
<script type="text/javascript">
var i = 0;
setInterval(function(){bird.className = "sprite " + 'bird' + ((i++) % 3);
}, 1000/10);
</script>

4. Web Animation API

element.animate(keyframes, options);

target.animate([{backgroundColor: '#00f', width: '100px', height: '100px', borderRadius: '0'},{backgroundColor: '#0a0', width: '200px', height: '100px', borderRadius: '0'},{backgroundColor: '#f0f', width: '200px', height: '200px', borderRadius: '100px'},
], {duration: 5000,fill: 'forwards',
});

使用web annimation API实现的动画:

function animate(target, keyframes, options) {const anim = target.animate(keyframes, options);return new Promise((resolve) => {anim.onfinish = function() {resolve(anim);}});
}(async function() {await animate(ball1, [{top: '10px'},{top: '150px'},], {duration: 2000,easing: 'ease-in-out',fill: 'forwards',});await animate(ball2, [{top: '200px'},{top: '350px'},], {duration: 2000,easing: 'ease-in-out',fill: 'forwards',});await animate(ball3, [{top: '400px'},{top: '550px'},], {duration: 2000,easing: 'ease-in-out',fill: 'forwards',});
}());

5. 总结

通过时间进行动画

更多推荐

360前端星计划—前端动画可以这么玩(月影)

本文发布于:2024-02-19 19:39:44,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1765494.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:月影   计划   动画

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!