html5 canvas 實現(xiàn)光線沿不規(guī)則路徑運動實例代碼
文章主要介紹了html5 canvas 實現(xiàn)光線沿不規(guī)則路徑運動,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下。g讓動畫沿著不規(guī)則路徑運動查閱svg文檔后發(fā)現(xiàn),svg動畫...
文章主要介紹了html5 canvas 實現(xiàn)光線沿不規(guī)則路徑運動,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下。
g讓動畫沿著不規(guī)則路徑運動
查閱svg文檔后發(fā)現(xiàn),svg動畫運動有兩種實現(xiàn)方式,且都非常簡單,但對于100%實現(xiàn)設(shè)計師給出的效果有很大的距離
使用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法隨路徑進行沒有規(guī)律的變化
使用stroke-dasharray和stroke-dashoffset讓路徑邊線動起來
stroke-dasharray:設(shè)置shap和text 邊框虛線的實線長度與實線之間的間隔(虛線長度)
stroke-dashoffser:設(shè)置邊框線條相對于默認位置的偏移(正值:向左,負值:向右)
<!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 ,此時實線和實線間隔一樣</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,此時實線和是實線間隔的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è)置線條的光線效果,即實線的陰影和實線的漸變效果(漸變區(qū)域需隨著偏移路徑的變化而變化)
canvas實現(xiàn)線條延不規(guī)則路徑運動
線條實現(xiàn)
對于不規(guī)則路徑,如果直接用畫線條的方式實現(xiàn)光線,需要計算每一個開始點和結(jié)束點的位置,中間還可能存在轉(zhuǎn)折點,計算起來非常麻煩,不可取
故這邊采取canvas組合圖形的模式,取線條和一個圖形重疊部分(類似于燈罩)來實現(xiàn)光線效果
組合前
組合后
<!DOCTYPE html>
????<html>
????<head>
????????<title>canvas實現(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],
????????????}
????????});//獲取每個點位置
????????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è)置圖形組合方式 默認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)我們實現(xiàn)好線條剩下就需要讓線條動起來,由于線條是通過燈罩的方式來實現(xiàn)的,讓線條運動只需要讓燈罩動起來就好
<!DOCTYPE html>
????<html>
????<head>
????????<title>canvas實現(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],
????????????}
????????});//獲取每個點位置
????????var step = 3;
????????var x1, x2, y1, y2;//確定路徑中最大最小點
????????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)');?? //定義漸變線起點顏色
????????????grd.addColorStop(1, 'rgba(255, 255, 255, 1)');? //定義漸變線結(jié)束點的顏色
????????????ctx.strokeStyle = grd;
????????????ctx.stroke();
????????}
????????//設(shè)計合適的初始線條狀態(tài)
????????var arrLine = Array(10).fill(0).map((item, inx) => {
????????????return {
????????????????x: x1 - 20 * inx,
????????????????y: y1 + 30,
????????????}
????????});
????????//隨時間變化圖形路徑
????????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è)置圖形組合方式 默認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>
這種實現(xiàn)方式也有一定的條件限制,那就是路徑可大體抽象成為一個有一定規(guī)律的圖型或者線條,比如上面demo中路徑可抽象成為一個矩形的兩邊,或者是2條連接的直線
我們必須從沒有具體規(guī)則的路徑中抽象出一個大體的規(guī)則,不同路徑規(guī)則不同
上面的例子就是將不規(guī)則路徑抽象成了一個直角的規(guī)則路徑
可優(yōu)化點
這邊找到了2個可優(yōu)化的點
1.時間方向上: 為了讓動畫消耗較小,代碼中的定時器已經(jīng)用的是requestAnimationFrame, 但是由于光線的特殊性(自帶模糊效果),為了性能更加,嘗試了2次requestAnimationFrame調(diào)用一次繪圖的方式,效果較前者未有明顯區(qū)別
2.繪圖方向上: 從上圖可發(fā)現(xiàn),燈罩每次只圈出路徑的一部分,故繪圖中不需要每次都繪制全部路徑,只需要找出燈罩前后的路徑點,將這一段路徑繪制出來就好
坑點
在完成這個動動畫效果之后遇到一個至今原因不明的bug,隨著屏幕放置時間的變長,動畫越來越慢,打開任務(wù)管理器,未見內(nèi)存泄漏或者CPU使用率過高。打開performance,發(fā)現(xiàn)頁面調(diào)幀嚴(yán)重,屏幕幀數(shù)越來越低,單個Frame CPU time越來越長,范圍來看,script和render和paint耗時未發(fā)生線性變化,只有system時間越來越來長,越來越長,期望能被大佬告知原因
一開始
到后來
解決的辦法較為...,光線每循環(huán)一個周期,我銷毀了之前的canvas并新建了canvas,上層規(guī)避了system time不知道為什么越來越長的問題
chrome版本:80.0.3987.163(正式版本) (64 位)
到此這篇關(guān)于html5 canvas 實現(xiàn)光線沿不規(guī)則路徑運動的文章就介紹到這了,更多相關(guān)canvas 光線不規(guī)則運動內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,
- html5中使用hotcss.js實現(xiàn)手機端自適配的方法
- HTML5網(wǎng)頁遮罩層 + Iframe實現(xiàn)界面自動顯示的示例代碼
- HTML5通過navigator.mediaDevices.getUserMedia調(diào)用手
- HTML5中的網(wǎng)絡(luò)存儲實現(xiàn)方式
- HTML5離線存儲Manifest原理及使用詳解
- HTML5調(diào)用手機發(fā)短信和打電話功能
- HTML5標(biāo)簽HTMLCollection和NodeList的區(qū)別詳解
- HTML5網(wǎng)頁中iframe與window.onload如何使用詳解
- HTML5網(wǎng)頁body設(shè)置自適應(yīng)全屏示例代碼
- Html5 canvas中width、height和style的寬高區(qū)別詳解
html5中使用hotcss.js實現(xiàn)手機端自適配的方法
文章主要介紹了html5中使用hotcss.js實現(xiàn)手機端自適配的方法,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下Html5頁面在手機端做自適配是很常見的技術(shù)需求,下面...
HTML5網(wǎng)頁遮罩層 + Iframe實現(xiàn)界面自動顯示的示例代碼
主要介紹了遮罩層 + Iframe實現(xiàn)界面自動顯示的示例代碼,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧前言這周由于科三的考試耽誤了兩天,提前一...
HTML5通過navigator.mediaDevices.getUserMedia調(diào)用手機攝像頭問題
文章主要介紹了HTML5通過navigator.mediaDevices.getUserMedia調(diào)用手機攝像頭問題,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下navigator.mediaDevices.getU...
HTML5中的網(wǎng)絡(luò)存儲實現(xiàn)方式
傳統(tǒng)方式使用document.cookie來進行存儲,但是由于其存儲的空間只有4KB左右,并且需要復(fù)雜的操作進行解析,給發(fā)開者帶來很多不便,為此,HTML5規(guī)范提出了網(wǎng)絡(luò)存儲的解決方案,本文通過...
HTML5離線存儲Manifest原理及使用詳解
文章主要介紹了H5離線存儲Manifest原理及使用,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧理解:離線存儲可以將站點的文件儲存在本地,在沒有網(wǎng)絡(luò)...
HTML5調(diào)用手機發(fā)短信和打電話功能
文章主要介紹了HTML5調(diào)用手機發(fā)短信和打電話功能,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下前言本來感覺用H5寫調(diào)用電話撥號功能和發(fā)送短信功能會很不好寫...
HTML5標(biāo)簽HTMLCollection和NodeList的區(qū)別詳解
文章主要介紹了HTML5中的HTMLCollection和NodeList的區(qū)別詳解,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧HTML5 HTMLCollection和NodeList的...
HTML5網(wǎng)頁中iframe與window.onload如何使用詳解
文章主要介紹了iframe與window.onload如何使用詳解,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。前言在項目上,需要等頁面加載完之后再執(zhí)行一...
HTML5網(wǎng)頁body設(shè)置自適應(yīng)全屏示例代碼
文章主要介紹了HTML5 body設(shè)置自適應(yīng)全屏,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧用什么代碼實現(xiàn)?不允許有白色底色產(chǎn)生,因為手機高度不一...
Html5 canvas中width、height和style的寬高區(qū)別詳解
文章主要給大家介紹了關(guān)于H5 canvas中width、height和style的寬高區(qū)別的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們...