本文首發於個人博客,這是個人github,歡迎star。 html
這篇博客是模仿nest.js實現一個demo
,由簡單到複雜,來一步步的實現它。這裏是效果預覽。個人github裏邊還有不少別的前端的demo
,喜歡的話能夠點個star
,你的支持就是個人動力。前端
實現一個半徑10px
的小球在500px*500px
的方塊中作直線運動,初始方向隨機,速度保持不變,碰撞到牆壁後反彈。
看下效果,思路很簡單,將小球定位在方塊中,設置xy
方向上的速度,每幀動畫給小球定位的值加上對應方向的速度值,在檢測到小球碰撞牆壁的時候,將對應方向的速度置爲反方向就能夠了。這裏是實現的代碼,沒有用到canvas
,可是思路一致。git
畫出一個彈射的小球很簡單,那怎麼用多個小球實現nest.js這樣的效果呢。這樣的特效確定不能用Dom
直接作,太耗費性能,也作不出來,這時就顯露出canvas
的強大之處了。
一樣的,用canvas
生成多個彈來彈去的小球。首先不要管鼠標如何吸附這些小圓點,只作小球之間的連線。在每次繪製小球以前,判斷一下它和以前的小球的距離是否是小於極限距離,小於就以它倆爲端點繪製一條線。代碼以下,思路都寫在註釋裏:github
const theCanvas = document.getElementById('theCanvas'), ctx = theCanvas.getContext('2d'), mix = 6000; //會產生連線的極限距離的平方 //將canvas鋪滿瀏覽器 let canvas_width = theCanvas.width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, canvas_height = theCanvas.height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, points = []; theCanvas.style = "position: fixed; top: 0px; left: 0px;"; //繪製函數,用requestAnimationFrame調用實現動畫 function draw() { //清屏 ctx.clearRect(0, 0, canvas_width, canvas_height); let i,pi,x_dist,y_dist; //遍歷點集合繪製線條 points.forEach((p, index) => { p.x += p.xa, //按指定速度移動 p.y += p.ya, p.xa *= p.x > canvas_width || p.x < 0 ? -1 : 1, p.ya *= p.y > canvas_height || p.y < 0 ? -1 : 1, ctx.fillRect(p.x - 0.5, p.y - 0.5, 1, 1); //繪製點,實際上是小方塊 //相似於握手問題,兩個點之間只繪製一次線 for(i = index + 1; i < points.length; i++) { pi = points[i]; x_dist = p.x - pi.x; y_dist = p.y - pi.y; dist = x_dist * x_dist + y_dist * y_dist; //判斷點之間的距離是否小於極限距離 if(dist < mix) { ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(pi.x, pi.y); ctx.stroke(); } } }),requestAnimationFrame(draw); } //隨機生成100個點 for(let i = 0; i < 100; i++ ) { let x = Math.random() * canvas_width, //初始座標 y = Math.random() * canvas_height, xa = 2 * Math.random() - 1, //x速度 ya = 2 * Math.random() - 1; //y速度 points[i] = {x, y, xa, ya}; } draw();
看下效果,醜陋,和人家的不同,很生硬。由於連線不是忽然出現忽然消失的,點和點之間的連線是漸漸的出現,而後漸漸消失的。給連線添加動態的屬性,用點和點的之間的距離來計算連線的粗細、透明度,在兩點距離比較遠的時候線會變淡,這樣看起來就舒服多了。面試
for(i = index + 1; i < points.length; i++) { pi = points[i]; x_dist = p.x - pi.x; y_dist = p.y - pi.y; dist = x_dist * x_dist + y_dist * y_dist; //根據兩點距離獲得一個參數w w = (mix - dist) / mix; //判斷點之間的距離是否小於極限距離 if(dist < mix) { ctx.beginPath(); //根據參數w設置連線寬度和透明度 ctx.lineWidth = w / 2; ctx.strokeStyle = `rgba(110,110,110,${w + 0.2})`; ctx.moveTo(p.x, p.y); ctx.lineTo(pi.x, pi.y); ctx.stroke(); } }
先是加入對鼠標的響應。在鼠標進入瀏覽器時添加鼠標這個點,不然移除。canvas
window.onmousemove = e => { e = e || window.event; current_point.x = e.clientX; current_point.y = e.clientY; }; window.onmouseout = () => { current_point.x = null; current_point.y = null; }; //將鼠標的點添加至點集合中 all_points = [...random_points,current_point];
要實現一個鼠標吸附粒子的效果,思路就是粒子和鼠標的距離在必定範圍內時,給粒子添加一個向着鼠標的速度,結果就好像是粒子受到鼠標的吸附同樣。這是一段鼠標吸附效果的核心代碼:瀏覽器
//當兩點距離小於極限距離時產生連線,當第二個點是鼠標所產生點時,粒子若是在範圍內就會產生向鼠標點的速度,實現吸附效果 dist < pi.max && (pi === current_point && dist >= pi.max / 2 && (p.x -= 0.03 * x_dist, p.y -= 0.03 * y_dist));
加入鼠標的點以後再作一些調整,獲得最終的代碼。dom
還能夠利用canvas
的getImageData
屬性,將圖片粒子化,作成輪播圖,點擊這裏預覽,主要思路是用getImageData
取到圖片像素點的信息,每隔一段取一個樣本,以這個樣本繪製粒子,繪製出相似於馬賽克同樣的圖片,而後給粒子加上運動的效果就能夠了,這裏是具體的代碼實現。函數
這篇博客到這就結束了, 這是個人github,歡迎來訪,歡迎star。