1. 程式人生 > 其它 >fabric.js動畫製作簡單的落雪效果播放

fabric.js動畫製作簡單的落雪效果播放

老規矩先上程式碼

  1 <template>
  2   <div class="animate">
  3     <el-button
  4       id="toggle"
  5       @keyup.space.native
  6       class="elBtn"
  7       :class="{ active: isFirst && autoPlay }"
  8       >▷</el-button
  9     >
 10     <canvas id="canvas"></canvas>
 11
</div> 12 </template> 13 14 <script> 15 import { fabric } from "fabric"; 16 export default { 17 data() { 18 return { 19 // radius/left/top/opacity is belong to circle 20 radius: [], 21 left: [], 22 top: [], 23 opacity: [], 24 // decide the tag of first pause
25 isFirst: false, 26 // autoPlay is the same statement with [playing] 27 autoPlay: false, 28 }; 29 }, 30 mounted() { 31 let playing = false; 32 let snowBallN = 100; 33 let cir = []; 34 35 this.radius = Array(snowBallN).fill(""); 36 this.left = Array(snowBallN).fill("");
37 this.top = Array(snowBallN).fill(""); 38 this.opacity = Array(snowBallN).fill(""); 39 40 const windowSize = { 41 width: "1000", 42 height: "600", 43 }; 44 45 // 產生靜態 canvas 46 const canvas = new fabric.StaticCanvas("canvas", { 47 height: windowSize.height, 48 width: windowSize.width, 49 }); 50 51 //animate(設定動畫屬性,動畫結束值,可選物件) 52 //可選物件為動畫詳細資訊,包括持續時間、回撥、緩動等 53 //在每個動畫幀上呼叫canvas.renderAll才能夠看到實際的動畫,重新渲染 54 55 // add snowBallN circle 56 for (let i = 0; i < snowBallN; i++) { 57 // cir Array is the gather of the single CIRCLE ANIMATION 58 cir[i] = new fabric.Circle({ 59 radius: getRandomInt(0.1, 1), 60 left: getRandomInt(0, windowSize.width), 61 top: getRandomInt(0, windowSize.height), 62 opacity: getRandomInt(0.1, 1), 63 fill: "#fff", 64 }); 65 // tag the last add cir[i] 66 cir[i].lastAdd = i === snowBallN - 1; 67 canvas.add(cir[i]); 68 // set animate when it achieve the last circle 69 playing && setAnimate(cir[i]); 70 } 71 console.log("Circle Array", cir); 72 73 // pause button 74 document.querySelector("#toggle").addEventListener("click", (e) => { 75 const targetEl = e.target; 76 this.isFirst = true; 77 78 // When playing is true, it should save the statement. 79 if (playing) { 80 targetEl.innerHTML = "▷"; 81 82 for (let i = 0; i < snowBallN; i++) { 83 this.radius[i] = canvas.getObjects()[i].radius; 84 this.left[i] = canvas.getObjects()[i].left; 85 this.top[i] = canvas.getObjects()[i].top; 86 this.opacity[i] = canvas.getObjects()[i].opacity; 87 } 88 // EveryTime, this array and the next is same, 89 // but the root of CANNOT SAVE is the simple CIRCLE. 90 // Just load every circle into the big cir Array, it's done. 91 // console.table("暫停Before Pause this.left", this.left.slice(0, 5)); 92 // console.table("Before Pause this.top", this.top.slice(0, 5)); 93 } else { 94 // Else, it should load in the shape what'll move. 95 targetEl.innerHTML = "▢"; 96 // console.log("this.left[0]", this.left[0]); 97 // think about the initial suitation, it should be divided. 98 // When radius/left/top/opacity array isn't null, it should reload in cir. 99 if (this.left[0]) { 100 for (let i = 0; i < snowBallN; i++) { 101 cir[i].set("radius", this.radius[i]); 102 // console.log("INNER DPACE", this.left[i]); 103 cir[i].set("left", this.left[i]); 104 cir[i].set("top", this.top[i]); 105 cir[i].set("opacity", this.opacity[i]); 106 } 107 } 108 // console.table("開始Before Play this.left", this.left.slice(0, 5)); 109 // console.table("Before Play this.top", this.top.slice(0, 5)); 110 // When it begin to play first, every circle need to set Animation. 111 canvas.getObjects().forEach((key) => setAnimate(key)); 112 } 113 this.autoPlay = !this.autoPlay; 114 115 // Change statement model 116 playing = !playing; 117 }); 118 // get Random number 119 function getRandomInt(min, max) { 120 return Math.floor(Math.random() * (max - min + 1)) + min; 121 } 122 123 // 設定動畫函式 124 function setAnimate(circle) { 125 // 變化半徑 126 circle.animate("radius", getRandomInt(0.1, 5), { 127 duration: getRandomInt(1000, 5000), 128 }); 129 // 變化透明度 130 circle.animate("opacity", getRandomInt(0.1, 0.9), { 131 duration: getRandomInt(1000, 5000), 132 }); 133 // 變化座標 134 circle.animate("left", getRandomInt(0, windowSize.width), { 135 easing: fabric.util.ease.easeInOutCubic, 136 duration: getRandomInt(1000, 5000), 137 }); 138 // 變化座標 139 circle.animate("top", getRandomInt(0, windowSize.height), { 140 // onChange and onComplete is two statement about the animation 141 // onChange decide the movement, 142 // onComplete decide the end action when the animation ends. 143 onChange: () => { 144 if (circle.lastAdd) playing && canvas.renderAll(); 145 }, 146 onComplete: () => playing && setAnimate(circle), 147 easing: fabric.util.ease.easeInOutCubic, 148 duration: getRandomInt(1000, 5000), 149 }); 150 } 151 }, 152 }; 153 </script> 154 <style> 155 .animate { 156 background-image: url(../assets/black.png); 157 position: absolute; 158 display: flex; 159 justify-content: center; 160 align-items: center; 161 } 162 #toggle { 163 position: absolute; 164 z-index: 999; 165 width: 200px; 166 height: 200px; 167 font-size: 100px; 168 } 169 .elBtn { 170 opacity: 0.4; 171 } 172 .active { 173 opacity: 0; 174 } 175 </style>
View Code

這個落雪效果是我仿照

Fabric.js 動畫 - 略知三二一 (321332211.com)

這個網頁的一百個圓隨機動畫製作的,加入了暫停與恢復播放的狀態儲存,優化了暫停播放按鈕效果,在第一次通過滑鼠點選播放後能夠使用空格控制播放。

做的還是有點麻瓜,過程也有點不撞南牆不回頭的意思,一直重複著一百個雪花的失敗,腦袋都不清醒了,晚上睡覺前忽然想到要控制下數量,化繁為簡,早上立馬就發現問題所在了!總之,還是要淡定~

效果如圖

人生到處知何似,應似飛鴻踏雪泥。