沒有最好,只有更好,如題所示,這篇文章只要是分享一個用Canvas 來實現的粒子運動效果。感覺有點標題黨了,但換個角度,勉強強算是炫麗吧,雖然色彩上與炫麗無關,但運動效果上還是算得上有點點炫的。不管怎麼樣,我們還是開始這個所謂的炫麗效果吧!
直接上程式碼,不懂可以看程式碼註解。估計就會看懂大概的思路了。
html 程式碼
<!DOCTYPE html><html lang=en><head><meta charset=UTF-8><title>Canvas 實現炫麗的粒子運動效果-雲庫前端</title><style>* { margin: 0; padding : 0;}html,body { width: 100%; height: 100%;}canvas { display: block; background: #000;}body::-webkit-scrollbar{ display: none;}.operator-box{ position: fixed; top: 0; left: 50%; border: 1px solid #fff; background: rgba(255,255,255,0.5) ; padding: 20px 10px; -webkit-transform: translateX(-50%); transform: translateX(-50%);}.back-type,.back-animate{ margin-right: 20px;}.flex-box{ display: flex; justify-content: center; align -items: center;}#input-text{ line-height: 35px; width: 260px; height: 35px; background: rgba(0, 0, 0,0.7); color: #fff; font-size: 16px; border: none; outline: none; text-indent: 12px; box-shadow: inset 0 0 12px 1px (0,0,0,0.7);}#input-text::placeholder{ color: #ccc; line-height: 55px; height: 55px;}select{ -webkit-appearance: none; -moz-appearance: none; appearance: none; border: none; padding: 0px 20px 0px 6px; ; color: #fff; text-align: left; background: rgba(0, 0, 0,0.7) url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAICAYAAAAx8TU7AAAAOUlEQ…R4gPgWEIMAiOYBCS4C8ZDAIrBq4gigNkztQEFMi6AuQHESAPPUSP4C8ZDAIrBq4gigNkztQEF米background-size: 5px 8px; box-shadow: inset 0 0 12px 1px rgba(0,0,0,0.7);}</style></head><body><div class=operator-box><div class =flex-box> <div class=back-type>散開類型: <select name= id=selectType> <option value=back>歸位</option> <option value=auto>隨機</option> </select> </div> <div class=back-animate>散開效果(對歸位有效): <select class =back-dynamics id=selectDynamics> <option value=spring>dynamics.spring</option> <option value=bounce>dynamics.bounce</option> <option value=forceWithGravity>dynamics.forceWithGravity</option> <option value=gravity>dynamics.forceWithGravity</option> <option value=gravity>dynamics.gravity</option> <option value=easeInOut>dynamics.easeInOut</option> <option value=easeIndy=easeInOut>dynamics.easeInOut</option> <option value=easeIndy option> <option value=easeOut>dynamics.easeOut</option> <option value=linear>dynamics.linear</option> </select> </div> <div class=input-box><input type=text placeholder=輸入漢字後回車id=input-text></div>< /div></div><script src=dynamics.min.js></script><script src=index.js></script><script>var iCircle = new Circle();</script></body></html>
HTML 程式碼不多,只要是幾個操作元素。這裡一看就明白。不費過多口舌。讓我們來看看本文的主角JavaScript 程式碼,不過,在看程式碼前,我們不妨先聽聽實現這個效果的想法:
JavaScript 程式碼中使用了三個Canvas 畫布,this.iCanvas(主場)、this.iCanvasCalculate(用來計算文字寬度)、this.iCanvasPixel(用於畫出文字,並從中得到文字對應的像素點的位置座標) 。
this.iCanvasCalculate 和this.iCanvasPixel 這兩個無需在頁面中顯示出來,只是輔助作用。
下面就獻上棒棒的JS 實作程式碼
function Circle() { var This = this; this.init(); this.generalRandomParam(); this.drawCircles(); this.ballAnimate(); this.getUserText(); // 視窗改變大小後,生計算並取得畫面window.onresize = function(){ This.stateW = document.body.offsetWidth; This.stateH = document.body.offsetHeight; This.iCanvasW = This.iCanvas.width = This.stateW; This.iCanvasH = This.iCanvas.height = This.stateH; This.ctx = This.iCanvas.getContext(2d); }}/ / 初始化Circle.prototype.init = function(){ //父元素寬高this.stateW = document.body.offsetWidth; this.stateH = document.body.offsetHeight; this.iCanvas = document.createElement(canvas); // 設定Canvas 與父親元素同寬高this.iCanvasW = this.iCanvas.width = this. 與父親元素同寬高this.iCanvasW = this.iCanvas.width = this. ; this.iCanvasH = this.iCanvas.height = this.stateH; // 取得2d繪畫環境this.ctx = this.iCanvas.getContext(2d); // 插入到body 元素中document.body.appendChild(this.iCanvas); this.iCanvasCalculate = document.createElement(canvas); // 用於保存計算文字寬度的畫布this.mCtx = this.iCanvasCalculate.getContext(2d); this.mCtx.font = 128px 微軟雅黑; this.iCanvasPixel = document.createElement(canvas); this.iCanvasPixel.setAttribute(style,position:absolute;top:0;left:0;); this.pCtx = null; // 用於繪畫文字的畫布// 隨機產生圓的數量this.ballNumber = ramdomNumber(1000, 2000); // 儲存所有小球的陣列this.balls = []; // 儲存動畫中最後一個停止運動的小球this.animte = null; this.imageData = null; this.textWidth = 0; //儲存生成文字的寬度this.textHeight = 150; // 儲存生成文字的高度this.inputText = ; // 儲存使用者輸入的內容this.actionCount = 0; this.ballActor = []; // 儲存生成文字的粒子this.actorNumber = 0; // 儲存生成文字的粒子數量this.backType = back; // 歸位this.backDynamics = ; // 動畫效果this.isPlay = false; // 標識(在生成文字過程中,不能再產生)}// 渲染出所有圓Circle.prototype.drawCircles = function () { for(var i=0;i<this.ballNumber;i++){ this.renderBall(this.balls[0]); }}// 取得使用者輸入文字Circle.prototype.getUserText = function(){ This = this; // 儲存this 指向ipu = document.getElementById(input-text); ipu.addEventListener(keydown,function(event){ if(event.which === 13){ // 如果是回車鍵ipu.value = ipu.value.trim(); // 去頭尾空格var pat = /[/u4e00-/u9fa5]/; // 中文判斷var isChinese = pat.test(ipu.value); if(ipu.value.length !=0 && isChinese){ This.inputText = ipu.value; }else{ alert(請輸入漢字); return; } if(This.isPlay){ return } This.getAnimateType(); This.getTextPixel(); This.isPlay = true; } });}/ / 計算文字的寬Circle.prototype.calculateTextWidth = function () { this.textWidth = this.mCtx.measureText(this.inputText).width;}// 取得文字像素點Circle.prototype.getTextPixel = function () { if(this.pCtx){ this.pCtx.clearRect(0,0,this.textWidth ,this.textHeight); } this.calculateTextWidth(this.inputText); this.iCanvasPixel.width = this.textWidth; this.iCanvasPixel.height = this.textHeight; this.pCtx = this.iCanvasPixel.getContext(2d); this.pCtx.font = 128pxx 微軟雅黑; #FF0000; this.pCtx.textBaseline = botom; this.pCtx.fillText(this.inputText,0,110); this.imageData = this.pCtx.getImageData(0,0,this.textWidth,this.textHeight).data; this.this. this.textWidth,this.textHeight);}//取得文字粒子像素點位置Circle.prototype.getTextPixelPosition = function (width,height) { var left = (this.iCanvasW - width)/2; var top = (this.iCanvasH - height)/2; var space = 4; this.actionCount = 0; for(var i=0;i<this.textHeight;i+=space){ for(var j=0;j<this.textWidth;j+=space){ var index = j*space+i*this.textWidth*4; if(this.imageData[index] == 255){ if(this.actionCount<this .ballNumber){ this.balls[this.actionCount].status = 1; this.balls[this.actionCount].targetX = left+j; this.balls[this.actionCount].targetY = top+i; this.balls[this.actionCount].backX = this.balls[this.actionCount].x; this.balls[this.actionCount].backY = this. balls[this.actionCount].y; this.ballActor.push(this.balls[this.actionCount]); this.actionCount++; } } } this.actorNumber = this.ballActor.length; } this.animateToText();}// 粒子移動到指定位置Circle.prototype.animateToText = function(){ for(var i=0;i<This.actorNumber;i++){ dynamics. animate(This.ballActor[i], { x: this.ballActor[i].targetX, y: this.ballActor[i].targetY },{ type: dynamics.easeIn, duration: 1024, }); } setTimeout(function(){ This.ballbackType(); },3000);}// 粒子原路回傳Circle .prototype.ballBackPosition = function(){ for(var i=0;i<This.actorNumber;i++){ var ball = This.ballActor[i]; dynamics.animate(ball, { x: ball.backX, y: ball.backY },{ type: dynamics[this. backDynamics], duration: 991, complete:this.changeStatus(ball) }); }}//取得類型|動畫效果Circle.prototype.getAnimateType = function() { var selectType = document.getElementById(selectType); var selectDynamics = document.getElementById(selectDynamics); this.backType = documenttion. value; this.backDynamics = selectDynamics.options[selectDynamics.options.selectedIndex].value;}// 重設散開Circle.prototype.ballbackType = function(){ if(this.backType == back){ this.Packosition(this.backType); else{ this.ballAutoPosition(); } this.ballActor = [];}// 隨機散開Circle.prototype.ballAutoPosition = function(ball){ for(var i=0;i<this.actorNumber;i++){ this.changeStatus(this.ballActor[i]) }}/ / 更改小球狀態Circle.prototype.changeStatus = function(ball){ ball.status = 0; if(this.isPlay == true){ this.isPlay = false; }}// 隨機產生每個圓的相關參數Circle.prototype.generalRandomParam = function(){ for(var i=0;i<this.ballNumber;i++){ var ball = {}; ball.size = 1; // 隨機產生圓半徑// 隨機產生圓心x 座標ball.x = ramdomNumber(0+ball.size, this.iCanvasW-ball.size); ball.y = ramdomNumber(0+ball.size, this.iCanvasH-ball.size); ball.speedX = ramdomNumber(-1, 1); ball.speedY = ramdomNumber(-11 , 1); this.balls.push(ball); ball.status = 0; ball.targetX = 0; ball.targetY = 0; ball.backX = 0; ball.backY = 0; }}// 改變圓的位置Circle.prototype.changeposition = function(){ for(var i=0;i<this.ballNumber;i++ ){ if( this.balls[i].status == 0){ this.balls[i].x += this.balls[i].speedX; this.balls[i].y += this.balls[i].speedY; } }}// 畫圓Circle.prototype.renderBall = function(ball){ this.ctx. fillStyle = #fff; this.ctx.beginPath(); // 這一定要加this.ctx.arc(ball.x, ball.y, ball.size, 0, 2 * Math.PI); this.ctx.closePath(); // 這個一定要加this.ctx.fill();}// 小球碰撞判斷Circle.prototype.collision = function( ball){ for(var i=0;i<this.ballNumber;i++){ if(ball.x>this.iCanvasW-ball.size || ball.x<ball.size){ if(ball.x>this.iCanvasW-ball.size){ ball.x = this.iCanvasW-ball.size; }else{ ball.x = ball.size; } ball. speedX = - ball.speedX; } if(ball.y>this.iCanvasH-ball.size || ball.y<ball.size){ if(ball.y>this.iCanvasH-ball.size){ ball.y = this.iCanvasH-ball.size; }else{ ball.y = ball.size; } ball.speedY = - ball.speedY; } } }// 開始動畫Circle.prototype.ballAnimate = function(){ var This = this; var animateFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; (function move(){ animte = animateFrame(move); This.ctx.clearRect(0, 0, This.iCanvasW, This.iCanvasH) ; This.changeposition(); for(var i=0;i<This.ballNumber;i++){ This.collision(This.balls[i]); This.renderBall(This.balls[i]); } })();}// 生成一個隨機數字function ramdomNumber(min, max) { return Math.random() * (max - min) + min;}
看了程式碼估計也只是心裡炫了一下,也沒有讓你想把這個東西做出來的慾望,為此我知道必需得讓你眼睛心服口服才行。線上DEMO: 動感的粒子範例。
人無完人,代碼也一樣。看起來運作順暢的程式碼也或多或少有一些瑕疵,日前這個效果還只支援中文。英文的話,我得再努力一把,不管怎麼樣,英文後面一定是會加入來的,只是時間問題了。還有程式碼中用於標記是否可再次執行生成文字的屬性:this.isPlay ,還是一點瑕疵,this.isPlay 的狀態更改沒有準確的在粒子歸位的那一瞬間更改,而是提前更改了狀態。但這個狀態不會影響本範例效果的完整實現。
這個例子中用到了dynamics.js 函式庫,主要是用到它裡面的一些運動函數,讓粒子動起來更感人一些,僅此而已。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。