用 Canvas 編織璀璨星空圖

2021-02-08 web前端學習圈
△ 是新朋友嗎?記得先點web前端學習圈關注我哦~


是不是還蠻酷的呢?本文我們就來一點一點分析怎麼實現它!


分析


首先我們看看這個效果具體有那些要點。首先,這麼炫酷的效果肯定是要用到 Canvas 了,每個星星可以看作為一個粒子,因此,整個效果實際上就是粒子系統了。此外,我們可以發現每個粒子之間是相互連接的,只不過離的近的粒子之間的連線較粗且透明度較低,而離的遠的則相反。


開始 Coding


HTML 部分


這部分我就簡單放了一個 標籤,設置樣式使其填充全屏。


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


然後為了讓所有元素沒有間距和內補,我還加了一條全局樣式:


 * {

      margin: 0;

      padding: 0;

}


JavaScript 部分


下面我們來寫核心的代碼。首先我們要得到那個 canvas 並得到繪製上下文:


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

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

var mousePos = [0, 0];


緊接著我們聲明兩個變量,分別用於存儲「星星」和邊:


var nodes = [];

var edges = [];


下一步,我們做些準備工作,就是讓畫布在窗口大小發生變化時重新繪製,並且調整自身解析度:


window.onresize = function () {

    canvasEl.width = document.body.clientWidth;

    canvasEl.height = canvasEl.clientHeight;

 

    if (nodes.length == 0) {

      constructNodes();

    }

 

    render();

};

 

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


我們在第一次修改大小後構建了所有節點,這裡就要用到下一個函數(constructNodes)了


這個函數中我們隨機創建幾個點,我們用字典對象的方式存儲這些點的各個信息:


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);

      });

    });

  }


為了實現後面一個更炫酷的效果,我給第一個點加了一個 drivenByMouse 屬性,這個點的位置不會被粒子系統管理,也不會繪製出來,但是它會與其他點連線,這樣就實現了滑鼠跟隨的效果了。


這裡稍微解釋一下 radius 屬性的取值,我希望讓絕大部分點都是小半徑的,而極少數的點半徑比較大,所以我這裡用了一點小 tricky,就是用概率控制點的半逕取值,不斷調整這個概率閾值就能獲取期待的半徑隨機分布。


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


假設我們之前連接了 A、B兩點,也就是外側循環是A,內側循環是B,那麼在下一次循環中,外側為B,內側為A,是不是也會創建一條邊呢?而實際上,這兩個邊除了方向不一樣以外是完全一樣的,這完全沒有必要而且佔用資源。因此我們在 addEdge 函數中進行一個判斷:


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);

    }

  }


至此,我們的準備工作就完畢了,下面我們要讓點動起來:


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;

  }


看到這麼一大段代碼不要害怕,其實做的事情很簡單。這是粒子系統的核心,就是遍歷粒子,並且更新其狀態。更新的公式就是


v = v + a

s = s + v


a是加速度,v是速度,s是位移。由於我們這裡不涉及加速度,所以就不寫了。然後我們需要作一個邊緣的碰撞檢測,不然我們的「星星」都無拘無束地一點點飛~走~了~。邊緣碰撞後的處理方式就是讓速度矢量反轉,這樣粒子就會「掉頭」回來。


還記得我們需要做的滑鼠跟隨嗎?也在這處理,我們讓第一個點的位置一點一點移動到滑鼠的位置,下面這個公式很有意思,可以輕鬆實現緩動:


x = x + (t - x) / factor


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


好了,回到主題。我們在上面的函數中處理完了一幀中的數據,我們要讓整個粒子系統連續地運轉起來就需要一個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();

    });

  }


常規的 Canvas 繪圖操作,注意 beginPath 一定要調用,不然你的線就全部穿在一起了… 需要說明的是,在繪製邊的時候,我們先要計算兩點距離,然後根據一個閾值來判斷是否要繪製這條邊,這樣我們才能實現距離遠的點之間連線不可見的效果。


到這裡,我們的整個效果就完成了。如果不明白大家也可以去我文章開頭放的 repo 離去看完整的源碼。Have fun!!

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

聲明:文章著作權歸作者所有,如有侵權,請聯繫小編刪除。

相關焦點

  • 最酷美術生,用黑白兩個顏色畫出星空圖,受到了視覺的衝擊
    最酷美術生,用黑白兩個顏色畫出星空圖,受到了視覺的衝擊學美術跟其他的藝術不太一樣,美術是一個講究畫齡的藝術都知道美術生在畫畫的時候,會首先打上一層底色,而黑色基本上是最不會選擇的一種顏色,但是這個美術生用了。所以這樣就更加好奇,用了黑色打底的美術生會畫出一幅什麼樣的畫。
  • 用一天入門 canvas 和 JavaScript
    HTML canvas 用最簡單的方式,使web 開發者能夠通過JavaScript在網頁上繪製圖形。這樣,HTML 元素變得更加有趣。<canvas> 元素只是個容器,你總是需要使用 JavaScript 來準確繪製圖形。有人可能會說,我們總是可以添加這些點,也可以添加SVG,但這又會多麼有趣?
  • 用HTML5把Canvas緩衝區內容輸出到屏幕
    幾乎每個遊戲平臺都有不同版本的sokoban,但是我還沒有看到任何使用canvas元素的應用。${PageNumber}  canvas入門  讓我們僅使用單個canvas元素,開始創建我們的HTML5頁面。
  • Canvas框架-FabricJS簡介
    一個簡單的canvas demo通過fabric.Canvas獲取到canvas元素,並可以對canvas進行相應的設置,既可以通過獲取已存在的canvas元素,fabric也支持創建canvas元素,同樣也能對其進行相應操作與原生的異同之前有一個需求是點滑鼠左鍵並進行拖動,要實現實時在canvas上進行畫圖於是分別用元生canvas和fabric製作了該功能,接下來我們對比下兩者的異同
  • 手把手教你用Javascript製作隨機星星效果圖
    一、前言在瀏覽一些圖片網站的時候,經常會看到很多的漂亮的星空圖,比如,下面的圖片。其實這種星星圖片的效果,也可以通過html+css樣式和js的方式來實現。今天教大家如何實現星星圖的效果。
  • 你生日那天的宇宙,教你怎麼在NASA查自己生日那天的星空圖
    ,點擊第一個連結進入NASA的官網;第二步、在網頁下方輸入自己的生日,在第一個方框選擇生日的「月」,第二個方框選擇自己生日的「日」,然後點擊「SUBMIT」提交;第三步、點擊提交後,頁面背景就會變成自己的生日星空圖,如果想保存無水印版圖片,就點擊左下角圖片上的「See Full Image」查看全圖即可;同時這張小卡上還介紹了這張圖片拍攝的日期
  • File、Blob、dataURL 和 canvas 的應用與轉換
    (2) File 對象是特殊類型的 Blob,且可以用在任意的 Blob 類型的 context 中。比如:FileReader, URL.createObjectURL(), createImageBitmap(), 及 XMLHttpRequest.send() 都能處理 Blob 和 File。2.
  • 數據可視化 - canvas圖片壓縮
    needCompress) { maxW = image.naturalWidth maxH = image.naturalHeight } const canvas =document.createElement('canvas')
  • html2canvas 將代碼轉為圖片
    只是,不管是系統默認截屏還是截屏工具,都不是特別好用,尤其當代碼超過一屏的時候,還得分屏截取,而當你決定修改代碼的一部分的時候,有可能又要調整和重新截屏,這對於一大段一大段代碼的文章編輯來說是個噩夢一樣的體驗,而且截成一段段的代碼,對讀者也不友好。
  • html中繪製圖形標籤的詳細介紹
    本篇將介紹的是html中<canvas>標籤的用法,感興趣的朋友可以一起研究一下!在html5中,新增了很多實用的標籤,今天為大家介紹的是html5新增標籤<canvas>,<canvas>標籤只是一個容器,對內容並沒有樣式的更改。那它在html中有什麼用,接下來我們就一起來看看吧!
  • 王者榮耀3月29日的星空圖代表什麼 聖鬥士聯動皮膚達摩獅子座曝光
    而3月29日的星空圖又代表著什麼呢?之前跟大家提過,聖鬥士即將再次聯動王者,那麼這個星空圖代表的是一款聯動皮膚。從12個星座的星空圖中可以看出,上圖這個是獅子座的星空圖,所以王者榮耀將在3月29日推出獅子座皮膚。
  • Canvas入門實戰之實現一個圖形驗證碼
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫本文主要介紹用canvas實現圖形驗證碼的一些思路以及如何用javascript面向對象的方式更友好的實現canvas的功能,關於canvas的一些基本使用方法和API我整理了一個思維導圖,大家感興趣的可以參考學習
  • 15 個 HTML5 Canvas 應用欣賞
    今天,我們收集了15個優秀的canvas應用,每個開發者都應該知道。Sketchpad 是個優秀的 HTML5 應用,幫助我們創建有用的繪圖應用,使用JS和canvas元素。Sketchpad 的繪圖工具具有豐富的功能。
  • html2canvas - 動態生成海報的優質js庫
    如何把網頁上的內容用javascript來實現截圖?今天分享的html2canvas就可以。介紹在微信項目中經常會遇到動態生成海報的需求,Web前端合成圖片往往會使用canvas。canvas雖然強大,但用來合成海報非常繁瑣,一不小心就幾百行代碼了。
  • 開發者值得關注的HTML5新特性Canvas
    Canvas使用的是Canvas 2D API去繪製圖形的,這個API功能十分強大,而且大部分的瀏覽器都支持 2D canvas ——包括 Opera, Firefox, Konqueror 和 Safari。而且某些版本的 Opera 還支持 3D canvas ,firefox 也可以通過插件形式支持 3D canvas 。下面馬上來看Canvas的例子。
  • 交易之「道」——星空圖
    對星空圖的理解依然是最重要的邏輯基礎。翻閱舊文檔,看到這一篇。雖股過境遷,其理自現。請大家跟隨我彼時探求的思路,一起來搭建廣袤完善的星空圖。落實在交易中,我會根據盤面走勢來判斷當下所處的境況,通過分析盤面走勢來獲取對將來走勢有用的信息。 但獲取信息並不能立即幫助我們對後續走勢的概率進行有效判斷。各種信息往往是分散的、支離破碎的,甚至是對立的。比如這兩天大盤走出明確的放量下跌趨勢,為什麼我還要「逆勢」買入先河環保呢?這筆交易本質是不是賭博?
  • canvas文本繪製自動換行、字間距、豎排等實現
    p=7362一、canvas對文字排版的支持很弱和CSS相比,SVG以及canvas對文字排版的支持很弱。在CSS中天然支持的文本自動換行,其他letter-sapcing字間距,writing-mode豎排等都是一個CSS屬性就可以實現。但是在canvas中,全部都不支持。
  • canvass 和 canvas,哪個有帆布的意思
    adverse 比 averse 多一個字母,它們意思完全不同;canvass[ˈkænvəs] 也比 canvas[ˈkænvəs] 多一個字母,它們的意思也截然不同,那麼哪個有帆布的意思呢?二、canvas 作名詞,意為「帆布,畫布,油畫」,例如:The back of a tiger could have been a blank canvas.老虎的背部可能是一塊空白的畫布。
  • canvas 入門實戰--邀請卡生成與下載
    canvas的用途非常的廣,特別是html5遊戲以及數據可視化這兩個方面。現在canvas給我的感覺就和css3一樣,可以不用太厲害,但是必須要會基礎的用法。但是以後對canvas的需求,肯定會越來越大。所以canvas很值得學習,而且學好canvas,就是很好的一個加分項。對於這篇文章,我也是以canvas初學者的角度寫的,會有很多改善的地方。
  • 前端特效,用Canvas畫一隻會跟著滑鼠走的小狗,文末有福利分享
    如果用一張gif,然後根據滑鼠的位置移動這張gif,那麼當滑鼠停下來小狗不動的效果就做不了,因為gif一直在循環播放代碼控制不了這個行為。所以這種簡單方案是不可行的。然後又想到之前用CSS的animation做過這種逐幀動畫:所以就有思路了,小狗的動畫也是使用逐幀的動畫,並且用JS控制它的播放。