canvas繪製動畫的技巧
阿新 • • 發佈:2021-06-29
我們拿下圖中的沿著線段軌跡移動的原點來舉例,怎麼來實現這個動畫!
1)定義路徑集合Path,裡面規定關鍵座標點如startPoint和endPoint,設定從startPoint移動到endPoint的時間duration。
如下json物件,我們有6段路徑,分別進行了定義。我們將下面這個列表集合命名為path。
1 [ 2 { 3 "startPoint": { 4 "x": 252.86249999999995, 5 "y": 191.39166666666665 6 }, 7 "endPoint": {8 "x": 252.86249999999995, 9 "y": 169.66666666666666 10 }, 11 "duration": 3000 12 }, 13 { 14 "startPoint": { 15 "x": 251.62499999999994, 16 "y": 169.66666666666666 17 }, 18 "endPoint": { 19 "x": 393.52499999999986,20 "y": 226.2833333333333 21 }, 22 "duration": 15000 23 }, 24 { 25 "startPoint": { 26 "x": 393.52499999999986, 27 "y": 226.2833333333333 28 }, 29 "endPoint": { 30 "x": 393.52499999999986, 31 "y": 427.075 32}, 33 "duration": 15000 34 }, 35 { 36 "startPoint": { 37 "x": 385.6874999999999, 38 "y": 420.4916666666667 39 }, 40 "endPoint": { 41 "x": 385.6874999999999, 42 "y": 407.2916666666667 43 }, 44 "duration": 3000 45 }, 46 { 47 "startPoint": { 48 "x": 385.6874999999999, 49 "y": 407.2916666666667 50 }, 51 "endPoint": { 52 "x": 125.8125, 53 "y": 421.94166666666666 54 }, 55 "duration": 15000 56 }, 57 { 58 "startPoint": { 59 "x": 126.6375, 60 "y": 421.94166666666666 61 }, 62 "endPoint": { 63 "x": 126.6375, 64 "y": 434.31666666666666 65 }, 66 "duration": 3000 67 } 68 ]
2)每次事件迴圈執行程式碼都會計算一個座標值
怎麼計算新座標?具體就是如下程式碼:
this.curTime += this.timeFreshTime; let currentX = Easing.Linear(this.curTime, this.movePath.startPoint.x, this.movePath.endPoint.x - this.movePath.startPoint.x, this.movePath.duration); let currentY = Easing.Linear(this.curTime, this.movePath.startPoint.y, this.movePath.endPoint.y - this.movePath.startPoint.y, this.movePath.duration);
利用當前時間,起點,終點。我們藉助時間曲線easing庫,裡面的計算專為動畫設計,還有我之前介紹過一個運算庫《Tween演算法及緩動效果》都是一樣的。
程式碼參考如下:
1 export class Easing { 2 // t: current time(當前時間), 3 // b: beginning value(初始值), 4 // c: chang in value (變化量), 5 // d: duration(持續時間) 6 static Linear = function (t, b, c, d) { return c * t / d + b; }; 7 static Quad = { 8 easeIn: function (t, b, c, d) { 9 return c * (t /= d) * t + b; 10 }, 11 easeOut: function (t, b, c, d) { 12 return -c * (t /= d) * (t - 2) + b; 13 }, 14 easeInOut: function (t, b, c, d) { 15 if ((t /= d / 2) < 1) return c / 2 * t * t + b; 16 return -c / 2 * ((--t) * (t - 2) - 1) + b; 17 } 18 }; 19 static Cubic = { 20 easeIn: function (t, b, c, d) { 21 return c * (t /= d) * t * t + b; 22 }, 23 easeOut: function (t, b, c, d) { 24 return c * ((t = t / d - 1) * t * t + 1) + b; 25 }, 26 easeInOut: function (t, b, c, d) { 27 if ((t /= d / 2) < 1) return c / 2 * t * t * t + b; 28 return c / 2 * ((t -= 2) * t * t + 2) + b; 29 } 30 }; 31 static Quart = { 32 easeIn: function (t, b, c, d) { 33 return c * (t /= d) * t * t * t + b; 34 }, 35 easeOut: function (t, b, c, d) { 36 return -c * ((t = t / d - 1) * t * t * t - 1) + b; 37 }, 38 easeInOut: function (t, b, c, d) { 39 if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b; 40 return -c / 2 * ((t -= 2) * t * t * t - 2) + b; 41 } 42 }; 43 static Quint = { 44 easeIn: function (t, b, c, d) { 45 return c * (t /= d) * t * t * t * t + b; 46 }, 47 easeOut: function (t, b, c, d) { 48 return c * ((t = t / d - 1) * t * t * t * t + 1) + b; 49 }, 50 easeInOut: function (t, b, c, d) { 51 if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b; 52 return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; 53 } 54 }; 55 static Sine = { 56 easeIn: function (t, b, c, d) { 57 return -c * Math.cos(t / d * (Math.PI / 2)) + c + b; 58 }, 59 easeOut: function (t, b, c, d) { 60 return c * Math.sin(t / d * (Math.PI / 2)) + b; 61 }, 62 easeInOut: function (t, b, c, d) { 63 return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; 64 } 65 }; 66 static Expo = { 67 easeIn: function (t, b, c, d) { 68 return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b; 69 }, 70 easeOut: function (t, b, c, d) { 71 return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; 72 }, 73 easeInOut: function (t, b, c, d) { 74 if (t == 0) return b; 75 if (t == d) return b + c; 76 if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b; 77 return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b; 78 } 79 }; 80 static Circ = { 81 easeIn: function (t, b, c, d) { 82 return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b; 83 }, 84 easeOut: function (t, b, c, d) { 85 return c * Math.sqrt(1 - (t = t / d - 1) * t) + b; 86 }, 87 easeInOut: function (t, b, c, d) { 88 if ((t /= d / 2) < 1) return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b; 89 return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b; 90 } 91 }; 92 static Elastic = { 93 easeIn: function (t, b, c, d, a, p) { 94 var s; 95 if (t == 0) return b; 96 if ((t /= d) == 1) return b + c; 97 if (typeof p == "undefined") p = d * .3; 98 if (!a || a < Math.abs(c)) { 99 s = p / 4; 100 a = c; 101 } else { 102 s = p / (2 * Math.PI) * Math.asin(c / a); 103 } 104 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; 105 }, 106 easeOut: function (t, b, c, d, a, p) { 107 var s; 108 if (t == 0) return b; 109 if ((t /= d) == 1) return b + c; 110 if (typeof p == "undefined") p = d * .3; 111 if (!a || a < Math.abs(c)) { 112 a = c; 113 s = p / 4; 114 } else { 115 s = p / (2 * Math.PI) * Math.asin(c / a); 116 } 117 return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b); 118 }, 119 easeInOut: function (t, b, c, d, a, p) { 120 var s; 121 if (t == 0) return b; 122 if ((t /= d / 2) == 2) return b + c; 123 if (typeof p == "undefined") p = d * (.3 * 1.5); 124 if (!a || a < Math.abs(c)) { 125 a = c; 126 s = p / 4; 127 } else { 128 s = p / (2 * Math.PI) * Math.asin(c / a); 129 } 130 if (t < 1) return -.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; 131 return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b; 132 } 133 }; 134 static Back = { 135 easeIn: function (t, b, c, d, s) { 136 if (typeof s == "undefined") s = 1.70158; 137 return c * (t /= d) * t * ((s + 1) * t - s) + b; 138 }, 139 easeOut: function (t, b, c, d, s) { 140 if (typeof s == "undefined") s = 1.70158; 141 return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; 142 }, 143 easeInOut: function (t, b, c, d, s) { 144 if (typeof s == "undefined") s = 1.70158; 145 if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b; 146 return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b; 147 } 148 }; 149 static Bounce = { 150 easeIn: function (t, b, c, d) { 151 return c - Easing.Bounce.easeOut(d - t, 0, c, d) + b; 152 }, 153 easeOut: function (t, b, c, d) { 154 if ((t /= d) < (1 / 2.75)) { 155 return c * (7.5625 * t * t) + b; 156 } else if (t < (2 / 2.75)) { 157 return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b; 158 } else if (t < (2.5 / 2.75)) { 159 return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b; 160 } else { 161 return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b; 162 } 163 }, 164 easeInOut: function (t, b, c, d) { 165 if (t < d / 2) { 166 return Easing.Bounce.easeIn(t * 2, 0, c, d) * .5 + b; 167 } else { 168 return Easing.Bounce.easeOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b; 169 } 170 } 171 } 172 }Easing
3)當前座標點已計算更新,此時在新位置處繪製白色點,我們的事件循序間隔設定的50ms,所有能夠產生平滑移動的效果。
4)實際需要幾個移動的白點,那就需要定義幾個path。如上gif圖中我們有兩個path,這兩個path初始化的時間是錯開的,所有才產生非同步移動的效果。
5)當一個path走完,再讓其從頭走(path的第一個startPoint endPoint),這樣不斷地迴圈下去。