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)

这个网页的一百个圆随机动画制作的,加入了暂停与恢复播放的状态保存,优化了暂停播放按钮效果,在第一次通过鼠标点击播放后能够使用空格控制播放。

注意点:每次animation动画暂停(onChange),如果不保存状态,就会从随机位置再次播放!←我设定的是这样的

做的还是有点麻瓜,过程也有点不撞南墙不回头的意思,一直重复着一百个雪花的失败,脑袋都不清醒了,晚上睡觉前忽然想到要控制下数量,化繁为简,早上立马就发现问题所在了!总之,还是要淡定~

效果如图

posted @ 2021-08-20 09:58  乐盘游  阅读(263)  评论(0编辑  收藏  举报