Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 91精品国产麻豆国产自产在线,国产精品高清视亚洲一区二区,成人激情视频网站

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          用HTML5的canvas來畫一個(gè)夢幻星空,來學(xué)習(xí)一下吧

          隨著HTML5的火熱,越來越多的人投入到HTML5開發(fā)中了,canvas作為HTML5中比較重要的一個(gè)元素,在很多官網(wǎng)的主頁面中被使用到。今天我們一起來看看如何使用canvas畫出一個(gè)夢幻的星空背景,還會有流星運(yùn)動(dòng)。

          本文的代碼已經(jīng)放到Github上了,感興趣的可以自取,Github地址如下。

          https://github.com/zhouxiongking/article-pages/blob/master/articles/starry/starry.html

          HTML5

          實(shí)現(xiàn)效果

          首先我們來看看通過canvas實(shí)現(xiàn)的星空效果圖,如下所示。

          效果圖

          代碼實(shí)現(xiàn)

          接下來我們看看這個(gè)效果是如何通過代碼一步步實(shí)現(xiàn)的。

          首先來看看頁面上的HTML代碼,只有一個(gè)Div元素。

          HTML代碼

          Javascript代碼

          首先我們需要定義一些常量,比如畫布的寬和高,星星數(shù)量,流星個(gè)數(shù)。在這個(gè)星空中流星其實(shí)是星星的一個(gè),只是添加了動(dòng)態(tài)效果。

          頁面初始化

          然后是設(shè)定一個(gè)定時(shí)器,通過一段隨機(jī)時(shí)間生成一個(gè)流星的索引號。

          流星索引號

          緊接著來看看生成一個(gè)星星的方法,該方法返回一個(gè)星星的各項(xiàng)參數(shù),包括x,y軸坐標(biāo),透明度,x,y軸偏移量。

          生成星星的參數(shù)

          然后是最重要的render方法,通過該方法可以將星星渲染至畫布上,我們將這個(gè)方法拆開看,首先是對流星的繪制,流星索引號通過上面metor方法獲得。

          畫流星

          然后是對于星星各項(xiàng)參數(shù)的處理,比如有的星星生成的點(diǎn)坐標(biāo)超出了屏幕寬高,有的透明度是負(fù)數(shù),都要將其處理成正常參數(shù)。

          各項(xiàng)參數(shù)判斷

          最后是在畫布中進(jìn)行繪制。

          畫布繪制

          至此,這個(gè)畫面效果的講解完畢,如果代碼正確的話,就可以看到文中出現(xiàn)的效果圖。

          結(jié)束語

          今天這篇文章主要是借助HTML5中的canvas畫出了一個(gè)夢幻星空的效果,你學(xué)會了嗎?

          天躺在床上刷抖音的時(shí)候,看見了一個(gè)馬克筆隨便畫星空的視頻,很有意思。


          先看效果:


          開始需求分析:

          1、漸變色的背景

          2、畫一顆樹和一些草

          3、水面的倒影

          4、隨便畫點(diǎn)星星

          5、畫一顆流星


          1、漸變色的背景

          先確定200*500的區(qū)域,使用css3的線性漸變屬性,依次深藍(lán)、淺藍(lán)、紫色、粉色、黃色畫出一個(gè)漸變色的背景。

          為了使背景更真實(shí),使用同樣的顏色順序,在不同的角度,加上一些模糊和透明。再畫一遍重疊起來。

          再重疊一層黑色,使畫布更暗一些。

          .bg-color {
            background-image: linear-gradient(170deg, #000093 13%, #9f35ff, #ff8000 70%, #f9f900 );
          }
          .bg-color2 {
            background-image: linear-gradient( 180deg, #000093 13%, #9f35ff, #ff8000 80%, #f9f900 );
            opacity: 0.3;
            filter: blur(6px);
          }
          .bg-color3 {
            background: rgba(0,0,0,.2);
          }


          2、畫一棵樹和草

          使用html來畫一棵樹的話,需要很多個(gè)節(jié)點(diǎn),性能和效果都很差。這里使用canvas來畫樹。

          畫樹的教程,公眾號出過好幾次了,這里就不在重寫了。

          基本原理就是,從一個(gè)點(diǎn)向一個(gè)方向畫一條直線。從終點(diǎn)開始,重新這個(gè)流程。期間可以修改一個(gè)角度畫出一分支。

          草就更加簡單。隨便在底部畫一些雜亂的直線。

          // 畫一棵樹
          function drawTree(x, y, deg, step, type) {
            var deg1 = step % 2 == 0 ? 0.1 : -0.1;
            var x1 = x + Math.cos(deg + deg1) * (step + 5) * 0.9;
            var y1 = y + Math.sin(deg + deg1) * (step - 1) * 0.9;
            ctx.beginPath();
            ctx.lineWidth = step / 3;
            ctx.moveTo(x, y);
            ctx.lineTo(x1, y1);
            ctx.stroke();
            if (step > 12) {
              ctx.arc(x, y, step / 6, 0, Math.PI * 2);
              ctx.fill();
            }
            if (step < 3 && Math.random() > 0.7) {
              var r = 2 + Math.random() * 2;
              ctx.arc(x1 + Math.random() * 3, y1 + Math.random() * 3, r, r, Math.PI + r);
              ctx.fill();
            }
            step--;
            if (step > 0) {
              drawTree(x1, y1, deg, step, type);
              if (step % 2 == 1 && step < 17)
                drawTree(x1, y1, deg + 0.2, Math.round(step / 1.13));
              if (step % 2 == 0 && step < 17)
                drawTree(x1, y1, deg - 0.2, Math.round(step / 1.13) );
            }
          }


          3、水面的倒影

          最簡單的做法,就是使用canvas.toDataUrl 拿到canvas的圖片數(shù)據(jù)。在底部放一個(gè)反轉(zhuǎn)的圖片就可以。

          我這里希望水面的倒影可以動(dòng)起來。

          新建一個(gè)canvas,使用ctx.getImageData拿到我們畫好的樹的像素點(diǎn)數(shù)據(jù)。

          使用正弦給像素的x軸做一些偏移,得到一個(gè)新的數(shù)據(jù)。put到倒影的canvas上。

          在使用requestAnimationFrame,做出一個(gè)流暢的左右擺動(dòng)的倒影動(dòng)畫。

          最后,在原數(shù)據(jù)基礎(chǔ)上,增加一些雜色,使得倒影有一些黑白的橫線,模擬水波的高亮。

          var startWave = 0 // 水波起始位置
          // 倒影增加水波紋效果
          function wave(star){
            var newImgData = ctxShadow.createImageData(200,150)
            var pos = 0
            var source = 0
            startWave += 0.2
            start = startWave
            for(var y = 0 ; y < CANVAS_HEIGHT ; y ++) {
              start += 0.5
              for(var x = 0 ; x < CANVAS_WIDTH ; x ++) {
                pos = (y * CANVAS_WIDTH + x) * 4
                source = (y * CANVAS_WIDTH + x + Math.round(Math.sin(start)* 1.5)) * 4
                newImgData.data[pos + 0] = imgData.data[source + 0];
                newImgData.data[pos + 1] = imgData.data[source + 1];
                newImgData.data[pos + 2] = imgData.data[source + 2];
                newImgData.data[pos + 3] = imgData.data[source + 3];
              }
            }
            ctxShadow.putImageData(newImgData,0,0)
            requestAnimationFrame(wave)
          }


          4、畫星空

          這個(gè)簡單,就不再寫代碼了,就隨意寫一些白色的div,隨機(jī)插入背景上。


          其實(shí)到這一步,已經(jīng)基本上完成了。


          5、加一些流星

          要畫流星,需要畫出一個(gè)漸漸變淡變窄的白線。

          這里偷了個(gè)懶,在視覺效果上,一個(gè)漸漸變淡的白線,人眼看到,就感覺漸漸變窄。

          這里使用白色加透明漸變,畫出一個(gè)流星的輪廓。加入從右到左動(dòng)畫效果。

          再加入一個(gè)外包的div,做一下旋轉(zhuǎn)和縮放。


          效果完成!!!!


          具體效果,建議查看原文。

          代碼倉庫地址:

          https://github.com/shb190802/html5

          演示地址:

          http://suohb.com/demo/win/starrySky.html

          不是還蠻酷的呢?利用周末時(shí)間我們來學(xué)習(xí)并實(shí)現(xiàn)一下,本文我們就來一點(diǎn)一點(diǎn)分析怎么實(shí)現(xiàn)它!


          分析


          首先我們看看這個(gè)效果具體有哪些要點(diǎn)。首先,這么炫酷的效果肯定是要用到 Canvas 了,每個(gè)星星可以看作為一個(gè)粒子,因此,整個(gè)效果實(shí)際上就是粒子系統(tǒng)了。此外,我們可以發(fā)現(xiàn)每個(gè)粒子之間是相互連接的,只不過離的近的粒子之間的連線較粗且透明度較低,而離的遠(yuǎn)的則相反。


          開始 Coding


          HTML 部分


          這部分我就簡單放了一個(gè) 標(biāo)簽,設(shè)置樣式使其填充全屏。


          <canvas height="620" width="1360" id="canvas" style="position: absolute; height: 100%;"/>


          然后為了讓所有元素沒有間距和內(nèi)部,我還加了一條全局樣式:


          * {

          margin: 0;

          padding: 0;

          }


          JavaScript 部分


          下面我們來寫核心的代碼。首先我們要得到那個(gè) canvas 并得到繪制上下文:


          var canvasEl = document.getElementById('canvas');

          var ctx = canvasEl.getContext('2d');

          var mousePos = [0, 0];


          緊接著我們聲明兩個(gè)變量,分別用于存儲“星星”和邊:


          var nodes = [];

          var edges = [];


          下一步,我們做些準(zhǔn)備工作,就是讓畫布在窗口大小發(fā)生變化時(shí)重新繪制,并且調(diào)整自身分辨率:


          window.onresize = function () {

          canvasEl.width = document.body.clientWidth;

          canvasEl.height = canvasEl.clientHeight;

          if (nodes.length == 0) {

          constructNodes();

          }

          render();

          };

          window.onresize(); // trigger the event manually.


          我們在第一次修改大小后構(gòu)建了所有節(jié)點(diǎn),這里就要用到下一個(gè)函數(shù)(constructNodes)了


          這個(gè)函數(shù)中我們隨機(jī)創(chuàng)建幾個(gè)點(diǎn),我們用字典對象的方式存儲這些點(diǎn)的各個(gè)信息:


          function constructNodes() {

          for (var i = 0; i < 100; i++) {

          var node = {

          drivenByMouse: i == 0,

          x: Math.random() * canvasEl.width,

          y: Math.random() * canvasEl.height,

          vx: Math.random() * 1 - 0.5,

          vy: Math.random() * 1 - 0.5,

          radius: Math.random() > 0.9 ? 3 + Math.random() * 3 : 1 + Math.random() * 3

          };

          nodes.push(node);

          }

          nodes.forEach(function (e) {

          nodes.forEach(function (e2) {

          if (e == e2) {

          return;

          }

          var edge = {

          from: e,

          to: e2

          }

          addEdge(edge);

          });

          });

          }


          為了實(shí)現(xiàn)后面一個(gè)更炫酷的效果,我給第一個(gè)點(diǎn)加了一個(gè) drivenByMouse 屬性,這個(gè)點(diǎn)的位置不會被粒子系統(tǒng)管理,也不會繪制出來,但是它會與其他點(diǎn)連線,這樣就實(shí)現(xiàn)了鼠標(biāo)跟隨的效果了。


          這里稍微解釋一下 radius 屬性的取值,我希望讓絕大部分點(diǎn)都是小半徑的,而極少數(shù)的點(diǎn)半徑比較大,所以我這里用了一點(diǎn)小 tricky,就是用概率控制點(diǎn)的半徑取值,不斷調(diào)整這個(gè)概率閾值就能獲取期待的半徑隨機(jī)分布。


          點(diǎn)都構(gòu)建完畢了,就要構(gòu)建點(diǎn)與點(diǎn)之間的連線了,我們用到雙重遍歷,把兩個(gè)點(diǎn)捆綁成一組,放到 edges 數(shù)組中。注意這里我用了另外一個(gè)函數(shù)來完成這件事,而沒有直接用 edges.push() ,為什么?


          假設(shè)我們之前連接了 A、B兩點(diǎn),也就是外側(cè)循環(huán)是A,內(nèi)側(cè)循環(huán)是B,那么在下一次循環(huán)中,外側(cè)為B,內(nèi)側(cè)為A,是不是也會創(chuàng)建一條邊呢?而實(shí)際上,這兩個(gè)邊除了方向不一樣以外是完全一樣的,這完全沒有必要而且占用資源。因此我們在 addEdge 函數(shù)中進(jìn)行一個(gè)判斷:


          function addEdge(edge) {

          var ignore = false;

          edges.forEach(function (e) {

          if (e.from == edge.from & e.to == edge.to) {

          ignore = true;

          }

          if (e.to == edge.from & e.from == edge.to) {

          ignore = true;

          }

          });

          if (!ignore) {

          edges.push(edge);

          }

          }


          至此,我們的準(zhǔn)備工作就完畢了,下面我們要讓點(diǎn)動(dòng)起來:


          function step() {

          nodes.forEach(function (e) {

          if (e.drivenByMouse) {

          return;

          }

          e.x += e.vx;

          e.y += e.vy;

          function clamp(min, max, value) {

          if (value > max) {

          return max;

          } else if (value < min) {

          return min;

          } else {

          return value;

          }

          }

          if (e.x <= 0 || e.x >= canvasEl.width) {

          e.vx *= -1;

          e.x = clamp(0, canvasEl.width, e.x)

          }

          if (e.y <= 0 || e.y >= canvasEl.height) {

          e.vy *= -1;

          e.y = clamp(0, canvasEl.height, e.y)

          }

          });

          adjustNodeDrivenByMouse();

          render();

          window.requestAnimationFrame(step);

          }

          function adjustNodeDrivenByMouse() {

          nodes[0].x += (mousePos[0] - nodes[0].x) / easingFactor;

          nodes[0].y += (mousePos[1] - nodes[0].y) / easingFactor;

          }


          看到這么一大段代碼不要害怕,其實(shí)做的事情很簡單。這是粒子系統(tǒng)的核心,就是遍歷粒子,并且更新其狀態(tài)。更新的公式就是


          v = v + a

          s = s + v


          a是加速度,v是速度,s是位移。由于我們這里不涉及加速度,所以就不寫了。然后我們需要作一個(gè)邊緣的碰撞檢測,不然我們的“星星”都無拘無束地一點(diǎn)點(diǎn)飛~走~了~。邊緣碰撞后的處理方式就是讓速度矢量反轉(zhuǎn),這樣粒子就會“掉頭”回來。


          還記得我們需要做的鼠標(biāo)跟隨嗎?也在這處理,我們讓第一個(gè)點(diǎn)的位置一點(diǎn)一點(diǎn)移動(dòng)到鼠標(biāo)的位置,下面這個(gè)公式很有意思,可以輕松實(shí)現(xiàn)緩動(dòng):


          x = x + (t - x) / factor


          其中 factor 是緩動(dòng)因子,t 是最終位置,x 是當(dāng)前位置。至于這個(gè)公式的解釋還有個(gè)交互大神 Bret Victor 在他的演講中提到過,視頻做的非常好,有條(ti)件(zi)大家一定要看看: Bret Victor – Stop Drawing Dead Fish


          好了,回到主題。我們在上面的函數(shù)中處理完了一幀中的數(shù)據(jù),我們要讓整個(gè)粒子系統(tǒng)連續(xù)地運(yùn)轉(zhuǎn)起來就需要一個(gè)timer了,但是十分不提倡大家使用 setInterval,而是盡可能使用 requestAnimationFrame,它能保證你的幀率鎖定在


          剩下的就是繪制啦:


          function render() {

          ctx.fillStyle = backgroundColor;

          ctx.fillRect(0, 0, canvasEl.width, canvasEl.height);

          edges.forEach(function (e) {

          var l = lengthOfEdge(e);

          var threshold = canvasEl.width / 8;

          if (l > threshold) {

          return;

          }

          ctx.strokeStyle = edgeColor;

          ctx.lineWidth = (1.0 - l / threshold) * 2.5;

          ctx.globalAlpha = 1.0 - l / threshold;

          ctx.beginPath();

          ctx.moveTo(e.from.x, e.from.y);

          ctx.lineTo(e.to.x, e.to.y);

          ctx.stroke();

          });

          ctx.globalAlpha = 1.0;

          nodes.forEach(function (e) {

          if (e.drivenByMouse) {

          return;

          }

          ctx.fillStyle = nodeColor;

          ctx.beginPath();

          ctx.arc(e.x, e.y, e.radius, 0, 2 * Math.PI);

          ctx.fill();

          });

          }


          常規(guī)的 Canvas 繪圖操作,注意 beginPath 一定要調(diào)用,不然你的線就全部穿在一起了… 需要說明的是,在繪制邊的時(shí)候,我們先要計(jì)算兩點(diǎn)距離,然后根據(jù)一個(gè)閾值來判斷是否要繪制這條邊,這樣我們才能實(shí)現(xiàn)距離遠(yuǎn)的點(diǎn)之間連線不可見的效果。


          到這里,我們的整個(gè)效果就完成了。如果不明白大家也可以去GitHub項(xiàng)目: CyandevToys / ParticleWeb去看完整的源碼。Have fun!!

          源自:http://www.jianshu.com/p/f5c0f9c4bc39

          聲明:文章著作權(quán)歸作者所有,如有侵權(quán),請聯(lián)系小編刪除。


          主站蜘蛛池模板: 亚洲欧洲无码一区二区三区| 国产经典一区二区三区蜜芽 | 极品少妇伦理一区二区| 国产香蕉一区二区精品视频| 波多野结衣一区二区三区88| 国产免费无码一区二区 | 亚洲一区电影在线观看| 无码毛片一区二区三区中文字幕 | 精品成人av一区二区三区| 国产91久久精品一区二区| 国产一区二区三区播放| 狠狠色综合一区二区| 成人精品视频一区二区三区不卡| 国产成人精品亚洲一区| 亚洲午夜一区二区电影院| 无码精品人妻一区二区三区中| 精品国产一区二区三区久久影院| 精品无码日韩一区二区三区不卡| 三上悠亚一区二区观看| 久久国产午夜精品一区二区三区| 国产大秀视频一区二区三区| 国产一区二区在线| 日韩成人无码一区二区三区| 一区二区三区在线观看| 国产在线精品一区二区| 国产精品一区二区四区| 久久久久久一区国产精品| 国产亚洲综合一区柠檬导航| 国产AV午夜精品一区二区入口 | 精品伦精品一区二区三区视频| 国产伦精品一区二区三区免费下载 | 人妻夜夜爽天天爽一区| 精品人妻AV一区二区三区| 91视频一区二区三区| 成人无码精品一区二区三区| 亲子乱AV视频一区二区| 呦系列视频一区二区三区| 丝袜人妻一区二区三区| 伊人无码精品久久一区二区| 深夜福利一区二区| 无码人妻精品一区二区三区66|