html5 canvas 實(shí)現(xiàn)光線沿不規(guī)則路徑運(yùn)動

svg讓動畫沿著不規(guī)則路徑運(yùn)動
查閱svg文檔后發(fā)現(xiàn),svg動畫運(yùn)動有兩種實(shí)現(xiàn)方式,且都非常簡單,但對于100%實(shí)現(xiàn)設(shè)計(jì)師給出的效果有很大的距離
使用offset-path偏移路徑和offset-roate偏移角度讓元素沿著不規(guī)則路徑走
<!DOCTYPE html> <html> <head> <title>offset-path/offset-roate</title> </head> <style type="text/css"> * { padding: 0; margin: 0; box-sizing: border-box; } body { background: #000; } .line { width: 80px; height: 3px; position: absolute; background: red; offset-path: path("M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500"); animation: move 10s linear infinite; } @keyframes move { 100% { offset-distance: 2000px; } } .line1 { position: absolute; left: 100px; width: 20px; height: 20px; border-radius: 50%; background: red; offset-path: path("M0,0a72.5,72.5 0 1,0 145,0a72.5,72.5 0 1,0 -145,0"); offset-rotate: 0deg; animation: load 1.8s cubic-bezier(0.86, 0, 0.07, 1) infinite; animation-delay: 0.147s; animation-fill-mode: forwards; } @keyframes load { from { offset-distance: 0; } to { offset-distance: 100%; } } </style> <body> <h2>路徑偏移</h2> <div class="line"></div> <svg width="100%" height="600px" version="1.0" id="svg1"> <path d="M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500" fill="#tranparent" stroke="#FFF"></path> </svg> <h2>角度偏移</h2> <div class="line1"> </div> </body> </html>
此種方式的限制是滾動元素?zé)o法隨路徑進(jìn)行沒有規(guī)律的變化
使用stroke-dasharray和stroke-dashoffset讓路徑邊線動起來
stroke-dasharray:設(shè)置shap和text 邊框虛線的實(shí)線長度與實(shí)線之間的間隔(虛線長度)
stroke-dashoffser:設(shè)置邊框線條相對于默認(rèn)位置的偏移(正值:向左,負(fù)值:向右)
<!DOCTYPE html> <html> <head> <title>stroke-dasharray/stroke-dashoffser</title> </head> <style type="text/css"> * { padding: 0; margin: 0; box-sizing: border-box; } body { background: #000; color: #fff; } .move { animation: moving 5s infinite; } @keyframes moving { 0% { stroke-dashoffset: 80px; } 100% { stroke-dashoffset: -1600px; } } </style> <body> <h2>設(shè)置stroke-dasharray</h2> <b>storke-dasharray設(shè)置為80 ,此時(shí)實(shí)線和實(shí)線間隔一樣</b> <svg width="100%" height="600px" version="1.0" id="svg1"> <path d="M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500" fill="#tranparent" stroke="#FFF" stroke-dasharray="80"></path> </svg> <b>storke-dasharray設(shè)置為80 320,此時(shí)實(shí)線和是實(shí)線間隔的1/4</b> <svg width="100%" height="600px" version="1.0" id="svg1"> <path d="M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500" fill="#tranparent" stroke="#FFF" stroke-dasharray="80 320"></path> </svg> <h2>設(shè)置stroke-dashoffset讓邊線相對于初始位置發(fā)生偏移</h2> <svg width="100%" height="600px" version="1.0" id="svg1"> <path d="M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500" fill="#tranparent" stroke="#FFF" stroke-dasharray="80 320" stroke-dashoffset="40"></path> </svg> <h2>通過設(shè)置stroke-dasharray 和 stroke-dashoffset讓邊線動起來</h2> <svg width="100%" height="600px" version="1.0" id="svg1"> <path d="M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500" fill="#tranparent" stroke="#FFF"></path> <path d="M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500" fill="#tranparent" stroke="red" stroke-dasharray="80 1600" stroke-dashoffset="0" class="move"></path> </svg> </body> </html>
此種方式通過邊框偏移的效果可以設(shè)置跟隨路徑的滾線條,但是無法設(shè)置線條的光線效果,即實(shí)線的陰影和實(shí)線的漸變效果(漸變區(qū)域需隨著偏移路徑的變化而變化)
canvas實(shí)現(xiàn)線條延不規(guī)則路徑運(yùn)動
線條實(shí)現(xiàn)
對于不規(guī)則路徑,如果直接用畫線條的方式實(shí)現(xiàn)光線,需要計(jì)算每一個(gè)開始點(diǎn)和結(jié)束點(diǎn)的位置,中間還可能存在轉(zhuǎn)折點(diǎn),計(jì)算起來非常麻煩,不可取
故這邊采取canvas組合圖形的模式,取線條和一個(gè)圖形重疊部分(類似于燈罩)來實(shí)現(xiàn)光線效果
組合前
組合后
<!DOCTYPE html> <html> <head> <title>canvas實(shí)現(xiàn)不規(guī)則路徑光效</title> </head> <style type="text/css"> body { background: #000; } #wrap { position: absolute; width: 1200px; height: 600px } </style> <body> <div id="wrap"> <canvas id="canvas" width="1200" height="600"></canvas> </div> </body> <script type="text/javascript"> var path = 'M 10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500'; var list = path.match(/([A-Z]([^A-Z]){1,})/g).map(item => { return { x: item.split(' ')[1], y: item.split(' ')[2], action: item.split(' ')[0], } });//獲取每個(gè)點(diǎn)位置 var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); ctx.strokeStyle = 'rgba(255,255,255,1)'; function drawPath() { ctx.lineWidth = 3; ctx.beginPath(); list.forEach(item => { if(item.action == 'M') ctx.moveTo(item.x, item.y); if(item.action == 'L') ctx.lineTo(item.x, item.y); }); ctx.stroke(); } drawPath(); function drawLine() { //設(shè)置圖形組合方式 默認(rèn)source-over ctx.globalCompositeOperation = "destination-in"; ctx.lineWidth = 60; ctx.beginPath(); ctx.moveTo(40, 80); ctx.lineTo(200, 80); ctx.stroke(); } drawLine(); </script> </html>
讓線條動起來
當(dāng)我們實(shí)現(xiàn)好線條剩下就需要讓線條動起來,由于線條是通過燈罩的方式來實(shí)現(xiàn)的,讓線條運(yùn)動只需要讓燈罩動起來就好
<!DOCTYPE html> <html> <head> <title>canvas實(shí)現(xiàn)不規(guī)則路徑光效</title> </head> <style type="text/css"> body { background: #000; } #wrap { position: absolute; width: 1200px; height: 600px } </style> <body> <div id="wrap"> <canvas id="canvas" width="1200" height="600"></canvas> </div> </body> <script type="text/javascript"> var path = 'M 10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500'; var list = path.match(/([A-Z]([^A-Z]){1,})/g).map(item => { return { x: item.split(' ')[1], y: item.split(' ')[2], action: item.split(' ')[0], } });//獲取每個(gè)點(diǎn)位置 var step = 3; var x1, x2, y1, y2;//確定路徑中最大最小點(diǎn) var timer; var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); ctx.strokeStyle = 'rgba(255,255,255,1)'; ctx.shadowColor = 'rgba(255,255,255,1)'; ctx.lineCap = 'round'; ctx.shadowBlur = 3; list.forEach(item => { x1 = !x1 || Number(item.x) < x1 ? Number(item.x) : x1; y1 = !y1 || Number(item.y) < y1 ? Number(item.y) : y1; x2 = !x2 || Number(item.x) > x2 ? Number(item.x) : x2; y2 = !y2 || Number(item.y) > y2 ? Number(item.y) : y2; }); function drawPath() { ctx.lineWidth = 3; ctx.beginPath(); list.forEach(item => { if(item.action == 'M') ctx.moveTo(item.x, item.y); if(item.action == 'L') ctx.lineTo(item.x, item.y); }); //添加光效漸變 var grd = ctx.createLinearGradient(arrLine[arrLine.length - 1].x, arrLine[arrLine.length - 1].y, arrLine[0].x, arrLine[0].y); grd.addColorStop(0, 'rgba(255, 255, 255, 0)'); //定義漸變線起點(diǎn)顏色 grd.addColorStop(1, 'rgba(255, 255, 255, 1)'); //定義漸變線結(jié)束點(diǎn)的顏色 ctx.strokeStyle = grd; ctx.stroke(); } //設(shè)計(jì)合適的初始線條狀態(tài) var arrLine = Array(10).fill(0).map((item, inx) => { return { x: x1 - 20 * inx, y: y1 + 30, } }); //隨時(shí)間變化圖形路徑 function getArrLine() { var isEnd arrLine = arrLine.map(item => { var x = item.x; var y = item.y; if(x < x2 - 30) { x = x + step > x2 -30 ? x2 - 30 : x + step; } else if(x == x2 -30 && y < y2) { y = y + step > y2 ? y2 : y + step; } else { isEnd = true; } return { x, y } }); isEnd && timer && cancelAnimationFrame(timer); } //繪制圖形 function drawLine() { //設(shè)置圖形組合方式 默認(rèn)source-over ctx.globalCompositeOperation = "destination-in"; ctx.lineWidth = 70; ctx.beginPath(); arrLine.forEach((item, inx) => { if(inx == 0) { ctx.moveTo(item.x, item.y); } else { ctx.lineTo(item.x, item.y); } }) ctx.stroke(); } function start() { ctx.clearRect(0, 0, 1200, 600); ctx.globalCompositeOperation = 'source-over'; drawPath(); drawLine(); getArrLine(); timer = requestAnimationFrame(start); } timer = requestAnimationFrame(start); </script> </html>
這種實(shí)現(xiàn)方式也有一定的條件限制,那就是路徑可大體抽象成為一個(gè)有一定規(guī)律的圖型或者線條,比如上面demo中路徑可抽象成為一個(gè)矩形的兩邊,或者是2條連接的直線
我們必須從沒有具體規(guī)則的路徑中抽象出一個(gè)大體的規(guī)則,不同路徑規(guī)則不同
上面的例子就是將不規(guī)則路徑抽象成了一個(gè)直角的規(guī)則路徑
可優(yōu)化點(diǎn)
這邊找到了2個(gè)可優(yōu)化的點(diǎn)
1.時(shí)間方向上: 為了讓動畫消耗較小,代碼中的定時(shí)器已經(jīng)用的是requestAnimationFrame, 但是由于光線的特殊性(自帶模糊效果),為了性能更加,嘗試了2次requestAnimationFrame調(diào)用一次繪圖的方式,效果較前者未有明顯區(qū)別
2.繪圖方向上: 從上圖可發(fā)現(xiàn),燈罩每次只圈出路徑的一部分,故繪圖中不需要每次都繪制全部路徑,只需要找出燈罩前后的路徑點(diǎn),將這一段路徑繪制出來就好
坑點(diǎn)
在完成這個(gè)動動畫效果之后遇到一個(gè)至今原因不明的bug,隨著屏幕放置時(shí)間的變長,動畫越來越慢,打開任務(wù)管理器,未見內(nèi)存泄漏或者cpu使用率過高。打開performance,發(fā)現(xiàn)頁面調(diào)幀嚴(yán)重,屏幕幀數(shù)越來越低,單個(gè)Frame CPU time越來越長,范圍來看,script和render和paint耗時(shí)未發(fā)生線性變化,只有system時(shí)間越來越來長,越來越長,期望能被大佬告知原因
一開始
到后來
解決的辦法較為...,光線每循環(huán)一個(gè)周期,我銷毀了之前的canvas并新建了canvas,上層規(guī)避了system time不知道為什么越來越長的問題
chrome版本:80.0.3987.163(正式版本) (64 位)
到此這篇關(guān)于html5 canvas 實(shí)現(xiàn)光線沿不規(guī)則路徑運(yùn)動的文章就介紹到這了,更多相關(guān)canvas 光線不規(guī)則運(yùn)動內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持腳本之家!
相關(guān)文章
- Microdata作為HTML5新增的一個(gè)特性,它允許開發(fā)者在HTML文檔中添加更多的語義信息,以便于搜索引擎和瀏覽器更好地理解頁面內(nèi)容,本文將探討HTML5中Microdata的使用方法以及2025-04-21
- 在HTML語法中,表格主要通過< table >、< tr >和< td >3個(gè)標(biāo)簽構(gòu)成,本文通過實(shí)例代碼講解HTML5表格語法格式,感興趣的朋友一起看看吧2025-04-21
- 這篇文章主要介紹了HTML5中使用媒體查詢和Flexbox進(jìn)行響應(yīng)式布局的方法,簡要介紹了CSS Grid布局的基礎(chǔ)知識和如何實(shí)現(xiàn)自動換行的網(wǎng)格布局,感興趣的朋友一起看看吧2025-04-21
基于Canvas的Html5多時(shí)區(qū)動態(tài)時(shí)鐘實(shí)戰(zhàn)代碼
本文介紹了如何使用Canvas在HTML5上實(shí)現(xiàn)一個(gè)多時(shí)區(qū)動態(tài)時(shí)鐘的web展示,通過Canvas的API,可以繪制出6個(gè)不同城市的時(shí)鐘,并且這些時(shí)鐘可以動態(tài)轉(zhuǎn)動,每個(gè)時(shí)鐘上都會標(biāo)注出對應(yīng)的2025-03-11HTML5 data-*自定義數(shù)據(jù)屬性的示例代碼
HTML5的自定義數(shù)據(jù)屬性(data-*)提供了一種標(biāo)準(zhǔn)化的方法在HTML元素上存儲額外信息,可以通過JavaScript訪問、修改和在CSS中使用,文章還介紹了高級用法,如存儲JSON數(shù)據(jù)、事2025-03-11HTML5中下拉框<select>標(biāo)簽的屬性和樣式詳解
在HTML5中,下拉框(<select>標(biāo)簽)作為表單的重要組成部分,為用戶提供了一個(gè)從預(yù)定義選項(xiàng)中選擇值的方式,本文將深入探討<select>標(biāo)簽的屬性、樣式,并重點(diǎn)介2025-02-27- 本文介紹了HTML5InputDatePicker對象表示HTML``元素,是HTML5中的新對象,介紹了日期、周、月份、時(shí)間、日期+時(shí)間、本地日期時(shí)間等不同類型的日期選擇器,感興趣的朋友一起看2025-02-17
- 本文介紹了HTML5中的超鏈接、相對路徑和圖片的使用方法,超鏈接可以創(chuàng)建指向另一個(gè)文檔或頁面內(nèi)部書簽的鏈接,相對路徑用于在同一服務(wù)器內(nèi)部跳轉(zhuǎn)頁面,圖片標(biāo)簽用于引入外部圖2025-02-17
- 本文介紹了HTML5超鏈接的創(chuàng)建方法,包括基本語法、創(chuàng)建圖像超鏈接的邊框去除方法以及錨點(diǎn)鏈接的使用,還討論了超鏈接的四種不同狀態(tài)(link、visited、hover、active)的CSS樣2025-02-17
HTML5使用details標(biāo)簽:展開/收縮信息
最近看一些技術(shù)網(wǎng)站發(fā)現(xiàn)了details 標(biāo)簽的妙用,這個(gè)不用js即可實(shí)現(xiàn)展開/收縮信息,很方便用來讓用戶先才答案,然后下面點(diǎn)擊再給出答案的效果,這里就為大家簡單介紹一下,2024-11-03