核心需求 避免变量污染前端
对于UI和逻辑的封装webpack
将重复费时的操做交给工具git
通用的抽象接口 线性++:web
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});
通用的抽象接口 角度 时间:chrome
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})
通用的接口 canvas:canvas
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); } }); } }
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); } }); } }
后面的具体例子就是数学公式结合前面的通用接口,找几个例子来记一下promise
贝塞尔轨迹:浏览器
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), });
周期运动:实际上周期运动的timing发生了变化性能优化
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); } }
用promise来写连续运动Ticker 其实目标就是使用promise作流程控制
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); } }); }); } }
下面就是上面promise trick的应用 小球弹跳
const down = lerp(setValue, {top: 100}, {top: 300}); const up = lerp(setValue, {top: 300}, {top: 100}); (async function() { const ticker = new Ticker(); // noprotect while(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)}); } })();
RAIL模型
关键指标:
针对浏览器渲染流程:页面是怎么样展现出来的?
JS->Style(这一步的输出是渲染树)->Layout(计算 产出盒模型)->paint(栅格化)->Composite(渲染层合并)
Layout 和 paint 是能够不出现的 因此会有各类场景
加载优化
渲染优化