JavaScript 彙整 | Creative Coding TW - 互動程式創作台灣站 https://creativecoding.in/tag/javascript/ 蒐集互動設計案例、教學與業界資源,幫助你一起進入互動程式創作的產業 Wed, 05 Jul 2023 04:49:37 +0000 zh-TW hourly 1 https://wordpress.org/?v=6.2.2 https://creativecoding.in/wp-content/uploads/2022/03/cropped-cct-logo-icon-2-32x32.png JavaScript 彙整 | Creative Coding TW - 互動程式創作台灣站 https://creativecoding.in/tag/javascript/ 32 32 未來 JavaScript 應用指標!不藏私 p5.js、生成式藝術與NFT 技術分享教學 https://creativecoding.in/2023/04/28/%e6%9c%aa%e4%be%86-javascript-%e6%87%89%e7%94%a8%e6%8c%87%e6%a8%99%ef%bc%81%e4%b8%8d%e8%97%8f%e7%a7%81-p5-js%e3%80%81%e7%94%9f%e6%88%90%e5%bc%8f%e8%97%9d%e8%a1%93%e8%88%87nft-%e6%8a%80%e8%a1%93/ Fri, 28 Apr 2023 08:39:02 +0000 https://creativecoding.in/?p=3643 JavaScript 未來全攻略!包括不藏私實際操作 Processing 和 p5.js,以及解密如何在 Artblocks 和 Opensea 等 NFT 平台上架作品。我們還會介紹連結硬體 OSC 和 socket,以及 MaxMSP 中 JS 模組的撰寫和基於 Tensorflowjs 的機器學習,帶領創作者一步步了解這些工具和技術,以便可以更好地創建自己的生成式藝術和互動作品,滿滿 JavaScript 乾貨讓創作者一次帶回家!

這篇文章 未來 JavaScript 應用指標!不藏私 p5.js、生成式藝術與NFT 技術分享教學 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>

哲宇老師榮幸受邀在 2021 年臺灣 JavaScript 開發者年會( JavaScript Developer Conference ,簡稱 JSDC )開講 JavaScript 在生成式、演算藝術與 NFT 的應用。 JavaScript 開發者年會是台灣最大的 JavaScript 年度性技術研討會, 提供台灣中高階 JavaScript 技術人才與世界最新 JavaScript 相關技術討論與分享。

此次分享將涵蓋到生成式藝術和互動領域的常用工具,包括不藏私實際操作 Processing 和 p5.js,以及解密如何在 Artblocks 和 Opensea 等 NFT 平台上架作品。我們還會介紹連結硬體 OSC 和 socket,以及 MaxMSP 中 JS 模組的撰寫和基於 Tensorflowjs 的機器學習,帶領創作者一步步了解這些工具和技術,以便可以更好地創建自己的生成式藝術和互動作品,滿滿 JavaScript 乾貨讓創作者一次帶回家!

JS的發展與瀏覽器虛擬化

JavaScript 在 p5.js 或是 即時 Web Base Ide 這些工具上,都能快速幫助藝術家和工程師快速的開發,甚至是結合網頁特效的 Library。當這些前端的東西都有了後,創作者通常會連結硬體。以前連接硬體,我們可能需要就是再額外安裝一些軟體或直接寫一些需要編譯的執行檔,現在你可以透過比如說像 OSC 與 socket,或者是像 web USB 這類協議直接連接硬體。在其他的軟體生態系裡面也逐步方便,比如說 Max MSP,也可以用 JS 來撰寫模組,甚至像 Tensorflowjs 的機器學習以及 ml5.js,也可以把機器學習跑在前端,然後最後再用 Electron 把它包裝成跨平臺的應用程式,都十分流暢。

JavaScript 在近年來的快速發展和優化,特別是 V8 引擎的改進和像 M1 晶片這樣的新技術的加入,使得JavaScript 的執行速度得到了大幅提升,從而使得 JavaScript 在處理一些複雜的任務,例如大量的particle 模擬或者是電腦繪圖等方面,也能夠有較好的表現。在這樣的情況下,像 Processing 和 p5.js 這樣的 JavaScript 框架也因其易學易用,而逐漸成為許多創作者和開發者的首選。這些框架提供了一個簡單的方式來設計和實現各種視覺化效果,從而使得創作者可以更輕鬆地進行創作,而不必擔心性能問題。

V8 在十年間的快速成長(圖片來源

生成式藝術與互動的常用工具

在創作生成是藝術跟互動的常用工具,第一個首選就是 p5.js 。 p5.js 可以視為 Processing 的 JavaScript 版本,它可以在網頁瀏覽器中運行。在 Processing 和 p5.js 中,可以使用不同的圖形和動畫函數,例如線條、形狀、色彩等,來創建各種視覺效果。或是使用鍵盤、滑鼠和觸控螢幕等輸入方式來創建互動應用程式。現在也有很多可以快速實作 p5.js 的 web ID,以下簡介給各位,第一個是 open processing 也是哲宇老師最常用的平臺,Open processing 使用上對初學者的設計師或工程師非常友善,像是在介面上以視覺為主,canvas 本身已幫創作者準備好,可以直接撰寫 p5.js 簡化的程式語言在上,並即時看見其渲染圖應用效果。比起 web canvas api 需要準備基礎的結構,可以讓創作者在最短的時間內執行 prototype,快速將生產環境建構出來。只要使用簡易語法,即可在畫布上建立不同的效果,例如:

  • createCanvas():建立畫布
  • fill():填上顏色
  • stroke():線條粗細
  • rect():建立方形
  • ellipse():建立圓形
  • mouseXmouseY:利用滑鼠 X 軸與 Y 軸的移動來控制圖形呈現的效果
  • rotate():旋轉
  • sin()cos():旋轉的角度

直述式的程式語言,與公開的文件參考資料讓使用者能夠直覺式的撰寫,讓大家可以更快速的進行創作。假使在使用上想要更嚴謹,或是引用更多的函式庫,哲宇老師推薦使用 CodeSandbox ,其與他者的差異性在於可以引入 Npm ,有點像是一個在遠端的虛擬機,可直接加入特定想使用的函式,檔案結構完整,甚至可以建立自己的模板,讓撰寫嚴謹,也符合個人化的專案設定。再來是 Codepen ,純粹使用 web canvas api,相較於 p5.js 較繁雜,但使用的工具本身並無優劣,而是需要依照每個人不同的的使用情境以及需求等等,去做創作上的使用。

在創作生成式藝術上,哲宇老師最常使用的方式是 p5.js 再加上 glsl 的 shader,產出效能大約為2至3小時一張創作便能完成,以下幾張較代表性的創作圖與各位做分享:

NFT 210612 Chaos Dancer #Classic #1(圖片來源

創作上,哲宇老師經常使用留下筆跡的創作手法,在畫布上設定畫布大小、需要出現的圖形與顏色,並且記得藏起背景功能,因為背景功能在 draw 底下為在每一次重複將東西清掉,所以無法顯示圖形軌跡,如下示範:

function setup() {
  createCanvas(windowWidth, windowHeight);
}

function draw() {
  // background(220);

  fill("blue");

  push();
    fill(255, mouseY, 50);
    translate(mouseX, mouseY);
    rotate(frameCount / 50);
    rect(0, 0, 200, 200);
  pop();

  ellipse(0, 0, 200, 200);
}

而 Chaos Dancer 就是以類似產生大量的 particle,讓它們隨著時間旋轉並逐漸變小,再加上 sin cos 等力場,並將軌跡留下,形成畫布,並再疊上不同材質讓創作有更不一樣的感覺。

NFT 200506 Loop Mountain #Classic #1(圖片來源

就是單純用迴園功能,讓圓圈往漸小的往上長,與隨機的大小變化去執行。

NFT 210913 Sweet Dreams #Classic #1(圖片來源

除了留下軌跡外,哲宇老師也分享了另一種常使用的創作手法,變化的遞迴切割。

變化的遞迴切割示範(圖片來源

半圓型線條,再加上大小不同的色塊與質感組成,跳脫一般遞迴切割的相同模式,形成較新穎的氛圍。在此創作上,主要使用 divideSq() 去形成遞迴切割的部分,並且設定在不同機率下形成的多樣變化。哲宇老師分享因自己的工程背景,在創作上常使用一些數學物理相關概念,也說明到其實遞迴、粒子或是物理模擬等等進行創作。諸如此類的視覺特效也有不同的資料庫提供大家參考使用,像是專門製作 3D 的 Three.js 、圖像處理與 Shader,具有像素等級處力效能的 Pixi.js 、向量操作專門戶 Paper.js 以及類似於早期的 Flash,專門製作小型遊戲的 Phaser.js

Artblocks – 區塊鏈與生成式藝術的整合

生成式藝術與 Artblocks 的關係到底為何呢?先來簡單介紹 Artblocks 給大家。 Artblocks 是賣生成式藝術的 NFT(非同質化代幣)平台,每個作品在平台上可能會有 500至1000個版本。但其限制是,平台希望程式也要在鏈上,所以只能接受使用一種  dependency 。當創作者完成創作後,每一個版本都會有所不同,但同時間因為透過了每個作品上的 hash ,轉換成亂數並給作品做使用。 在 opensea 上面會擁有這些不同版本的創作。

Artblocks 首頁(圖片來源

平台上編寫時,主要使用 xor 演算法,此演算法會依據不同的 seed ,也就是亂數位元作操作。操作完後,在以模擬電腦亂數表方式產生亂數, 如果次數固定,則意味著在相同 hash 的狀況下,一定可以在某些特定狀況下得到相同亂數,此方法可以在 random 中設定想要更改的數字或陣列。哲宇老師也使用了 Token Art Tools 執行 debug,透過左側項目欄調整各種比例以更改 hash 的組成,並自動提供給 script ,這樣就可以快速地看到作品在不同 hash 時的不同樣貌。(更多 Artblocks 的執行細節可參考此篇 >>【老闆週六來聊聊】吳哲宇 Artblocks Project – Electriz 製作分享

Token Art Tools 網站截圖

在上架 Artblocks 平台時,會要求將所有程式碼上架到主網上,而因此需付 1 至 2 ETH 左右。另外,在 mint 的同時,即會產生隨機的 hash ,也同時間將 preview 上架到 Opensea 上。所以,從 Artblock 到 Opensea 上的流程總體結論就是從 Hash 到 Random Number Generator ,最後在看要使用 Static output(靜態輸出) 或是 Dynamic output(動態輸出),並且會在這整個流程中的某一時刻擷取快照形成 preview 。生成式藝術的應用多變性,也直接的反應在創作中。像是要形成如上述複雜的圖樣,哲宇老師通常會使用變化角度 sin() 或 cos() 等去執行,或是假使作品要應用到不同載體上,如投影或是印製到硬體上,需要放大作品的像素密度,我們也只需要簡單設定 PixelDensity(),使作品在只要能透過瀏覽器進行的載體上,都能設定出自己所需要的圖片密度大小。

連接硬體 OSC 與 socket

以前在連結硬體上,都需要再透過 processing 進行傳輸,現在其實有多種不同方式可以使用,包含 p5.js 也可以幫你執行!比較為一般人常見的傳統 Open sound control(網絡音訊傳輸協定)有應用在 DJ 控制燈光及音樂的常見場景,使用 udp 加上 socket.io client,使前端網頁可以直接連結到 bridge 上接收 osc 的訊息,範例如下,哲宇老師使用過藉由賭博的方式去控制檯燈的作品。

哲宇老師連結硬體創作作品(圖片來源

此作品連結 MaxMSP,運用偵測到骰子上的點數以及有幾個骰子加上連結 socket 去控制檯燈的明亮度。另外,像是開源式平台 Arduino 只要在網站上就能編輯,或是 Johnny-Five ,一個基於 JavaScript 的機器人和物聯網(IoT)程式框架,允許開發人員使用高級程式語言來控制硬體。 Johnny-Five 設計可於包括 Arduino 、 Raspberry Pi 和 Particle 設備等各種硬體平台一起使用。

Johnny-Five 首頁(圖片來源

還有 Node-RED 以簡單易用的圖形化用戶界面,用戶可以通過拖拽節點並用連接線連接它們來創快速構建 IoT 應用程式。

Node-RED 首頁(圖片來源

經由這些新平台的介紹,我們可以知道,其實 Javascript 現在的語言邏輯更容易上手,能整合多種生態系,甚至是跨軟硬體執行,對創作者更增加了無限的可能。

MaxMSP 的 JS 模組撰寫

講到硬體與軟體的應用,就不得不提到 MaxMSP 了。 MaxMSP 為一款音樂創作、聲音處理和音樂表演的軟體,它可以讓用戶使用圖形化的方式編寫音樂程序。界面直觀,使用滑鼠和鍵盤就可以輕鬆地創建、編輯和控制音樂和聲音,所以 VJ 或是新媒體藝術家常運用此軟體進行創作。在使用上, MaxMSP 也提供了 Node for Max ,一個專門為 Max/MSP 環境開發的軟體,讓使用者也可以運用 Javascript 撰寫並轉譯成 MaxMSP 可以運用的語言。語言資源方面,除了 Max 的 api 外,也能使用像是 tonal,擁有諸多和弦及音符等相關處理函數能夠去做運算。

Node for Max 介面(圖片來源

前端機器學習及相關運用

以下與大家介紹更多相關資源,例如 TensorFlow.js ,是 Google 開發的一個開源 JavaScript 框架,它使得開發人員可以在網頁瀏覽器上使用並進行機器學習。強大的 JavaScript API ,可以用於在瀏覽器中創建、訓練和部署機器學習模型,除了可以在瀏覽器中運行這些模型外,還可以在瀏覽器中使用預訓練模型進行影像、聲音和自然語言處理等任務。

TensorFlow.js 首頁(圖片來源

使用上,則有 ml5js 函式庫的協助。函式庫中包含影像、聲音、姿勢和自然語言處理等多種模型,提供了更簡單的方式來使用機器學習。

ml5js 首頁(圖片來源

示範案例,使用函式庫進行臉部以及動作的追蹤創作,也可以依此應用延伸出更多商業價值,像是依據追蹤動作,留下軌跡或是特別觸發某些機關等互動式創作。

ml5js 示範創作截圖(圖片來源

或是透過 Google 的 Teachable Machine,在線上訓練不同的模型,輸出為 json 檔案後再透過 ML5 做載入,例如以下為說明左傾右傾的差別,並在最後到 p5.js 進行視覺化。

Teachable Machine 示範(圖片來源

上述所講到的瀏覽器端應用,皆可以使用 Electron 進行包裝,藉由封裝方式形成桌面板可應用程式,可運行於 Windows 、 macOS 和 Linux 等多個操作系統平台上,讓開發者可以使用熟悉的前端技術來開發桌面應用程式。

JS 在創作領域發展的展望與未來

瀏覽器的完整虛擬化和純雲端應用程式使得開發者可以在雲端環境中開發和部署應用程式,從而降低開發和維護成本,同時提高應用程式的可擴展性和安全性,進而也讓 Isomorphic JavaScript 的執行效能得到了大幅優化,並且提供了 App 包裝功能,使得開發者可以將應用程式打包成原生應用程式,運行在桌面和手機等平台上。加上高階特性和語法糖的成熟以及 TypeScript 的支持使得開發者可以更加容易地編寫高質量的程式碼,與 Package 生態系的豐富性和開源社區的活躍性,都為開發者提供了更多的選擇和支持。快速開發環境和不重複造輪子的潮流下,使得開發者可以更加專注於應用程式的邏輯,並且快速開發出高品質的應用程式,同時也可以減少開發成本和風險。

商業應用範例(圖片來源

總體而言, JavaScript 的發展帶來了許多新的機會和可能性,使得創作者可以更輕鬆地實現各種想法和概念。同時,像 Processing 和 p5.js 這樣的框架也為創作者提供了更多的工具和資源,使得他們可以更加專注於創作本身,而不必花費太多時間和精力在技術層面上。最後,經過本次分享你也躍躍欲試,等不及想要開始進行快速易上手的創作了嗎?趕快訂閱 老闆,來點寇汀吧。Boss, CODING please. Youtube 頻道搶先了解第一手消息,或是加入哲宇老師在 hahow 開設的課程,一起快速上手程式藝術撰寫!也歡迎對相關項目有興趣的同學可以至 墨雨設計 聊聊天喔 ~

此篇直播筆記由幫手熊柑協助整理。

這篇文章 未來 JavaScript 應用指標!不藏私 p5.js、生成式藝術與NFT 技術分享教學 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
外星圖像渲染語言?如何應用 Shader 跟 WebGL 達成絢麗視覺效果 https://creativecoding.in/2022/07/07/shader-webgl-intro/ Thu, 07 Jul 2022 08:32:00 +0000 https://creativecoding.in/?p=2909 想要在網頁中快速渲染出驚人的 3D 視覺效果和互動嗎?本篇文章老闆帶你快速入門了解 Shader、分享實作在專案中的經驗,就讓我們開始玩玩看 Shader 吧!

這篇文章 外星圖像渲染語言?如何應用 Shader 跟 WebGL 達成絢麗視覺效果 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
本篇文章老闆要來聊聊什麼是 Shader 、把 Shader 實作在專案中的經驗、以及如何開始玩玩看 Shader。 3D 技術逐漸應用於網頁上,瞭解 Shader 就可以透過 GPU ,以更快的速度渲染出各式各樣有趣的視覺效果和互動,快跟著老闆看下去吧!

吳哲宇的創作 Spike Mountains

閱讀完此篇文章,你會知道:

  • 什麼是 Shader
  • Shader 應用案例
  • 如何開始學習 Shader

相關資源

Shader 作品和必追蹤的藝術家


Shader

老闆第一次接觸 Shader,是在 Codrops 看到的 WebGL Distortion Hover Effects 這篇文章,第一印象覺得它根本就像是外星語言,因為它是非常底層的、類似 C++ 的程式語言。

滑鼠滑過物件的互動,對網頁工程師來說,大概就是簡單地在 css 寫上 &hover 然後可能換個背景圖片和顏色,再加上 transition 。但這篇文章中的 Demo 卻做出了各種讓人為之驚艷的效果,像是類似液體那樣 ㄍㄡˊ ㄍㄡˊ 的感覺、三角形漸層、稜鏡感、刷色感、鋸齒狀…等等。老闆原本想說,這大概需要研究個兩三天用很多行的程式碼才寫得出來,殊不知點開它的 code ,發現做出這麼酷炫視覺效果的程式碼,嚴格上來說只有短短的六行!

大概就是這個部分:

// hover-effect.js
void main() {
  vec4 disp = texture2D(disp, vUv);
  vec2 dispVec = vec2(disp.r, disp.g);

  vec2 uv = 0.5 * gl_FragCoord.xy / (res.xy) ;
  vec2 myUV = (uv - vec2(0.5))*res.zw + vec2(0.5);

  vec2 distortedPosition1 = myUV + getRotM(angle1) * dispVec * intensity1 * dispFactor;
  vec2 distortedPosition2 = myUV + getRotM(angle2) * dispVec * intensity2 * (1.0 - dispFactor);
  vec4 _texture1 = texture2D(texture1, distortedPosition1);
  vec4 _texture2 = texture2D(texture2, distortedPosition2);
  gl_FragColor = mix(_texture1, _texture2, dispFactor);
}
`;

為了瞭解他如何運作,老闆接觸到 Shadertoy 這個網站,裡面有類似剛剛那種 hover 效果的超級進階版應用,像是用 200 行左右的程式碼做出 超逼真海水 ,或者像這樣利用造點圖片加上扭曲旋轉效果即完成的 銀河 Galaxy

Shadertoy 上的範例作品銀河 Galaxy
Shadertoy 上的範例作品銀河 Galaxy

什麼是 Shader ?

What is a fragment shader?

Shaders are a set of instructions, but the instructions are executed all at once for every single pixel on the screen. That means the code you write has to behave differently depending on the position of the pixel on the screen. Like a type press, your program will work as a function that receives a position and returns a color, and when it’s compiled it will run extraordinarily fast. - The Book of Shaders

Shader 是繪製螢幕上內容的著色器,最重要的特性在於它是以每顆像素個別做渲染的方式運作,可以想像成活字印刷機,透過定義每顆像素在每個位置的表現組成一個完整畫面。

為什麼 Shader 渲染可以這麼快?

因為它不是用 CPU ,而是用 GPU 來運算。 CPU (中央處理器)通常被大家理解為是電腦的大腦,負責執行作業系統所需的指令與程序,它是線性的加工廠,一次可以處理一個工作。而 GPU (繪圖處理器)則是由許多更小也更專業的核心組成,所以我們就可以一次性地送出我們想處理的像素,一次性地處理完之後,再渲染出來。

用水管來比喻的話, CPU 就像一根大水管,但它一次只能處理一顆像素,而我們有堆積如山的像素等著被處理; GPU 則像是很多個水管並列,所以我們可以讓很多顆像素同時通過不同的小水管。

圖片來源:The Book of Shaders
圖片來源:The Book of Shaders

但是問題來了,我們要怎麼告訴這些不同的小水管,每顆像素該如何做處理呢?

在撰寫 Shaders 時使用的語言是 GLSL (OpenGL Shading Language) ,也稱做 GLslang ,是一個以 C 語言為基礎的高階著色語言。它讓我們可以跟 GPU 溝通,安排每個小水管該負責什麼工作。

這也是讓 Shaders 看起來不是很平易近人的原因。

首先,為了讓每根小水管能夠同時獨立運作,它們彼此之間的資料會是單向且無法溝通的,就像上方圖片示意那樣,資料處理的方向必須是相同的,而且每根水管都沒有辦法知道彼此處理完的像素變成什麼樣子。此外,因為 GPU 會讓工作接踵而至,換句話說會一直塞東西進水管,所以這些水管也就忙到沒辦法記得它們自己上一個工作的內容。

Each thread is not just blind but also memoryless. Besides the abstraction required to code a general function that changes the result pixel by pixel depending on its position, the blind and memoryless constraints make shaders not very popular among beginning programmers. - The Book of Shaders

Hello World

讓瀏覽器顯示 Hello World 通常是學新語言時會做的第一個練習,但是要在 GPU 領域渲染文字是件相對困難的事情,所以這個範例就用色塊來說明:

#ifdef GL_ES
precision mediump float;
#endif

uniform float u_time;

void main() {
  gl_FragColor = vec4(0,0,0,0); // red, green, blue, alpha
}

接著也可以嘗試代入變數uniform

我們可以透過 uniform 從 CPU 發送資料到 GPU ,且透過 uniform 宣告的變數會是全域且獨特的,可以在所有的 Shaders 中讀取。

#ifdef GL_ES
precision mediump float;
#endif

uniform float u_time;

void main() {
  gl_FragColor = vec4(abs(sin(u_time)),0.0,0.0,1.0);

  // gl_FragColor = vec4(abs(sin(mod(u_time, 0.5))),0.0,0.0,1.0); // 例如這樣寫可以讓閃爍變快
}

在 GLSL 中有提供許多計算數值的 functions ,像是 sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() and clamp() 等等,我們可以用這些函式搭配時間做出會不斷變動的圖形。

gl_FragCoord

除了像上面的範例那樣,用 uniform 宣告同樣的 input ,再用 default output vec4 gl_FragColor 呈現結果的做法外, GLSL 也給了我們 default input vec4 gl_FragCoord ,其中儲存了每個水管正在處理的 pixel 或 screen fragment 的座標,有了這個資訊,我們就可以針對每個座標做個別的處理,而這個情況下所使用的變數就不會叫做 uniform ,而會稱作 varying 。

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

void main() {
  vec2 st = gl_FragCoord.xy/u_resolution;
  // vec2 st = gl_FragCoord.xy/u_mouse; // 例如這樣可以用滑鼠改變漸層的位置

  gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}

在 Shader 裡面畫圖形是很困難的事情

因為用「點」連成「面」是一個相對高階的概念,在 GLSL 中如果要做出形狀,就得把每個邊的位置都計算出來之後再組合起來,才可以變成圖形。所以就會需要很多角度、三角函數、指數等等的數學計算。


Shader 可以做什麼?

把 Shader 做為材質來使用

可以用圖層的概念來想: 基礎圖形 + 一些 filter + 扭轉座標 = 看起來就會像有躁點的漩渦。

把 Shader 用在 3D 的模型上

用 Shaders 渲染一顆 3D 的球,也可以根據滑鼠位置做變化。

綜合應用

用 p5.js 畫一張動態圖片 + 把 Shaders 做成扭曲 filter。

如果註解掉 frag 標籤下, void main(){} 裡面的這三行程式碼,就可以得到沒有經過 shader 濾鏡的原始 p5.js 動態畫面:

distorted_st.x+=cnoise(vec3(st.x*5000.,st.y*3000.,u_time))/(1.+(sin(sqrt(st.y+u_time/20.)*50.)+1.)*500.)/2.; // 電視雜訊 1
distorted_st.y+=cnoise(vec3(st.x*5000.,st.y*3000.,u_time))/(1.+(sin(sqrt(st.y+u_time/10.)*50.)+1.)*1000.)/2.; // 電視雜訊 2
distorted_st.x += sin(distorted_st.y*(50.+sin(st.x)*20.)+u_time)*distorted_st.y*distorted_st.y/10.; // X方向的扭曲

Shader 就像是非常高功能的透鏡,但困難的點在於,我們要定義每個像素經過這個透鏡的行為。

與 3D 整合:Three.js – Displacement map

3D 軟體的渲染,像是 Unreal、Vray、Octane 等等軟體內的材質編輯器,跟 Shader 運用的概念都一樣,所以如果沒有要做到更底層的應用,使用 Shader 就像是在自討苦吃。因為把 Shader 做為 3D 環境的材質來使用,勢必得考慮光影的渲染,例如光打到某個點上,材質會如何變化、入射方向、反光效果等等,會更加複雜。

例如要做出像磁磚獲木紋那樣凹凸的效果,除了貼上紋理之外,還需要告訴軟體根據這個紋理渲染對應的陰影。 Shader 的 displacement map 就是透過指定一個畫面,算出對應的陰影和光線反射,讓整個畫面看起來更真實。

應用案例

日本藝術家 Sayama 的創作

日本藝術家 Sayama – 200426

實務應用

所有技術最重要的還是要能夠應用,譬如工作室願意把它用到上線的專案中,它才有意義。

實務上老闆把 Shader 拿來畫 OUTERNETS 的 Home 和 About us ,做出光暈和躁點效果,還有非常繽紛科幻可以跟滑鼠互動的旋轉球球。

Outernets

Outernets
Outernets

Outernets – About us

Outernets - About us
Outernets – About us

墨雨工作室也嘗試把 Shader 應用在 跟18天台灣生啤酒1起吃飯8! 這個活動網頁上,做出啤酒噴灑出來的動態扭曲效果。

跟18天台灣生啤酒1起吃飯8!
跟18天台灣生啤酒1起吃飯8!

如何開始學習 Shader ?

The Book of Shaders

這個網站用深入淺出的說明加上可以操作的範例,也提供了一個好用的 編輯器 (從網站首頁點選 Examples Gallery ,再點擊 “Hello World” 下面的色塊),可以先從這邊熟悉 Shader ,之後再嘗試放到自己的專案中。

Touch Designer

探索 Shader 也可以從 Touch Designer 開始,它幫我們把各種模組都寫好,讓我們不用寫任何程式碼,只要載入不同的圖片和效果就可以玩出各式各樣的變化。

Banana Test

例如老闆之前在嘗試的時候,把香蕉經過 transform 再合成到背景上面。

Banana test
Banana test

題外話:各位如果對 3D 有興趣的話,可以去玩玩看 Blender ,它是開源的免費軟體。

在 p5 裡面玩 Shader

ITP – p5js shaders 介紹了如何把 p5 和 Shader 結合使用,把 Shader 當成材質或者影像的即時處理。

老闆也在 OpenProcessing 製作了 Shader template ,基礎的架設都已經完成了,歡迎 fork 之後自己玩玩看囉!

簡單的 Demo

雜訊效果:可以用 rand() 給原本位置的像素一個隨機的位置資訊,就會讓畫面有看起來霧霧的效果。

// frag 標籤中的第 20 行開始
void main(){
  vec2 st = var_vertTexCoord.xy /u_resolution.xy;

  st.x += rand(st); // 霧霧的效果
  st.x += rand(st) / (1.+st.y*10.); // 上面會霧霧的,但越下面會越平滑

  vec3 color = vec3(st.x,st.y,1.0);
  float d = distance(u_mouse,st);
  color*=1.-d;
  gl_FragColor= vec4(color,1.0);
}

扭曲效果:用 sin() 給 y 位置的資訊(但因為底圖沒有東西所以看不太出來效果)

// frag 標籤中的第 20 行開始
void main(){
  vec2 st = var_vertTexCoord.xy /u_resolution.xy;

  st.y += sin(st.x/10.)/10.; // 像這樣

  vec3 color = vec3(st.x,st.y,1.0);
  float d = distance(u_mouse,st);
  color*=1.-d;
  gl_FragColor= vec4(color,1.0);
}

範例 test bubble 根據左側的底圖,做完扭曲效果後就變成右側:

void main(){
  vec2 st = var_vertTexCoord.xy /u_resolution.x;

  st.x += rand(st)/5.; // 模糊效果
  st.y += sin(st.x * 50.) /10. ; // 扭曲效果

  vec3 color = vec3(0.);
  float ang = atan(st.y-0.55,st.x-0.5) ;
  float r = sin(ang*5.+u_time*2.+u_mouse.y*3. +u_mouse.x*3.);
  vec2 displacemenetMap = vec2(cos(ang),sin(ang))*r /150. ;
  vec3 spray = texture2D(tex0,st + displacemenetMap -vec2(0.,0.2) ).rgb;

  if (!u_mouse_pressed){
    color += spray;
  }else{
    color = vec3(displacemenetMap,0.5)*500.;
  }

  gl_FragColor= vec4(color,1.0);
}

也可以拿上一格留下的畫面做扭曲,看起來就會像液體效果


結語

老闆覺得現在 3D 的趨勢應該會越來越往網頁發展,像 Autodesk 也出了 Fusion360 可以在網頁上製作 3D 物件,如果可以把這些效果應用在案子的網頁上,想必可以創作出更加吸睛的視覺。

五分鐘問答時間

Q. 會開 Shader 課程嗎?

感覺有點難募資達標,有點太小眾了😂

Q. 最近有看到什麼用 Shader 做的酷案例嗎?

可以在 awwwards 上找到很多厲害的範例!搭配 p5.js 或是 three.js 就可以用在像是 carousel (輪播)或是其他扭曲的效果。

Q. Node Editor 怎麼做?

可以使用現成函式庫 Rete.js ,把自己的 function 做成可以給前端使用的介面。但技術尚未非常成熟,用在實務要自己注意一下囉。

小範例:胎死腹中的 文章產生器 原本想說可以做一份教材,用模組編輯的方式組裝成 A, B, C… 多種不同的教材,就可以因材施教。但發現沒有特別方便,而且概念其實也蠻類似現在的筆記工具 Notion 。

老闆貼心提示: CodePen 不是工程師實務上會使用的

使用 CodePen 或者 OpenProcessing 等平台,主要是為了讓學生快速上手,不需要自己架設開發環境,如果想實作到 production ,還需要再去更進階了解 JavaScript 的模組化概念,以及使用 Webpack 或者 Rollup 之類的打包工具。


工商時間

互動藝術程式創作入門
互動藝術程式創作入門

《互動藝術程式創作入門》課程學生作品分享

Hahow 課程作業成果

課程加碼單元

此篇直播筆記由幫手 Yuan 協助整理

墨雨設計banner

這篇文章 外星圖像渲染語言?如何應用 Shader 跟 WebGL 達成絢麗視覺效果 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
用D3.js製作視覺化的日常作息方塊圖(直播筆記) https://creativecoding.in/2021/07/23/%e7%94%a8d3-js%e8%a3%bd%e4%bd%9c%e8%a6%96%e8%a6%ba%e5%8c%96%e7%9a%84%e6%97%a5%e5%b8%b8%e4%bd%9c%e6%81%af%e6%96%b9%e5%a1%8a%e5%9c%96/ Fri, 23 Jul 2021 02:03:00 +0000 https://creativecoding.in/?p=1308 D3.js工具可以把大筆的資料視覺化呈現於網頁上,本篇文章利用淺顯易懂的解說,一步步寫出如同Google日曆般的日常生活作息方塊圖。

這篇文章 用D3.js製作視覺化的日常作息方塊圖(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
本文翻自 [週四寫程式系列] – 來做視覺化日常作息的方塊圖吧!,若是對文章內容有疑問,或是想要老闆手把手帶你飛,都可以觀看影片跟著動手做,也附上這次成品

D3.js視覺化日常作息方塊圖成品
D3.js視覺化日常作息方塊圖成品

在資料科學中經常會面臨到的難關,就是資料看起來髒亂必須要重新整理,僅是物件或陣列,無法快速了解資料內容,透過資料視覺化,可以將資料轉換成另一種形式呈現,方便使用者閱讀。想做這個主題的緣由是因為,老闆會去紀錄每天的工作項目,想要利用這個工具,瞭解每天的工作內容及分布。

接下來老闆就要帶大家使用 D3 來達成資料視覺化,將原本無趣的資料,透過 D3 變成七個長條圖 ,一眼看出一周行事曆中每天各時段的工作分配。

製作的過程中可以分為兩個層次去思考:

  1. 一天的工作分配要怎麼畫?在繪製一天的行程中,帶大家認識 D3 繪製資料的方式。
  2. 一周的行程怎麼繪製?當完成一天後,我們只要將這個流程重複執行,就能產出一周的畫面。

此外,老闆也出了一個小題目給大家挑戰:如何實現多重選單中,項目不能重複。有興趣的同學可以在影片中,看看老闆如何解決這個問題。希望透過這篇文章讓大家發現 D3 真正的魅力,接著就讓我們拿起神奇的木槌 D3,將資料變得有趣吧!

如果對於老闆的教學方式有興趣,或是想看看老闆其他創作內容,歡迎大家去支持老闆的網頁程式入門或是網頁特效入門課程。

這次直播筆記會帶大家學會以下內容:

  • 利用 D3 繪製視覺化資料
  • 利用 js api ( map, filter )進行資料再處理
  • 製作一周工作項目產生器

事前準備

資料處理

《D3視覺化日常作息方塊圖》資料處理概念說明
《D3視覺化日常作息方塊圖》資料處理概念說明

資料視覺化之前,必須先準備好資料才能繼續,這邊會需要一周的行程,記錄每小時的工作項目。

第一步:先理解單日的行程,完成後就能將這個流程重複七次,達成需求。

第二步:將每天的行程(上圖左)群組化(上圖右),例如時段 1~3 都在做 b 這件事,則產出的資料會有類似以下結構。

var day = [
  {
    name: 'b',
    startTime: 1,
    endTime: 3,
  },
  ...
]

了解資料處理的邏輯後,需要原始的資料檔才能有下一步的畫面處理與呈現,可以利用關鍵字查詢 csv to json,將完成的一周行事曆檔案(excel)匯出成 csv 檔,再透過線上工具轉成 json 格式,這邊提供老闆使用的線上工具。如果想直接進入開發,除了可以使用下方老闆提供的24小時 raw_data 外,後面的內容老闆也會教大家亂數產生資料的方法。

var raw_data = [
  {
    "time": 0,
    "thing": "工作"
  },
  {
    "time": 1,
    "thing": "工作"
  },
  {
    "time": 2,
    "thing": "工作"
  },
  {
    "time": 3,
    "thing": "睡覺"
  },
  {
    "time":4,
    "thing": "睡覺"
  },
  {
    "time": 5,
    "thing": "睡覺"
  },
  {
    "time": 6,
    "thing": "睡覺"
  },
  {
    "time": 7,
    "thing": "工作"
  },
  {
    "time": 8,
    "thing": "睡覺"
  },
  {
    "time": 9,
    "thing": "睡覺"
  },
  {
    "time": 10,
    "thing": "做作業"
  },
  {
    "time": 11,
    "thing": "做作業"
  },
  {
    "time": 12,
    "thing": "工作"
  },
  {
    "time": 12,
    "thing": "工作"
  },
  {
    "time": 13,
    "thing": "工作"
  },
  {
    "time": 14,
    "thing": "睡覺"
  },
  {
    "time": 15,
    "thing": "睡覺"
  },
  {
    "time": 16,
    "thing": "工作"
  },
  {
    "time": 17,
    "thing": "工作"
  },
  {
    "time": 18,
    "thing": "睡覺"
  },
  {
    "time": 19,
    "thing": "散步"
  },
  {
    "time": 20,
    "thing": "工作"
  },
  {
    "time": 21,
    "thing": "工作"
  },
  {
    "time": 22,
    "thing": "散步"
  },
  {
    "time": 23,
    "thing": "工作"
  }]

開發環境

開發使用 codepen 線上撰寫程式碼,大家可以先將環境設定成跟老闆一樣,如果想知道較詳細的設定,可以參考成品老闆的設定。

會使用到 ES6 的語法,增加開發的效率

  • html: Pug
  • javascript:
    • Babel:為了加速開發的時間,我們會使用到 ES6 的語法
    • D3.js

接下來會使用到的 API:

這次專案會使用以下的 api,先重點整理給大家,不清楚的地方,可以一步步跟著老闆操作,了解每個 api 使用時機。

D3

  • .range(1, 5):產出陣列,陣列內為 [ 1, 2, 3, 4 ]
  • 選擇器
    • .select(“body”):取得 body 這個 tag (可填入其他值來取得其他目標物)。
    • .selectAll(“rect.num”):選擇畫面全部的 rect 且含有 class=”num” 的 tag。
  • .data(data1):將資料 data1 填入。
  • 資料綁定 tag 的方式:
    • .enter():綁定資料時,選取的元素不夠綁定時,該筆資料會被分配為 enter 類型,不足的 tag 可以搭配 .append 使用。
      • .append(“svg”):插入一個 svg tag (可選擇其他 tag)
    • .exit():綁定資料時,選取的元素多於資料時,該元素會被分配為 exit 類型。
      • .remove():刪除符合條件的元素,可以搭配 .exit() 刪除多餘的元素。
    • .update() 將新的資料直接覆蓋到對應的元素上。
  • .attr(“width”, 700) 為目標物賦予寬 700px 的屬性 (可填入其他值,賦予其他動態屬性,例如:邊框、顏色、x 座標…等)。
  • .domain(ary1):可以與 .range(ary2) 搭配,將定義的資料 ary1 對應到 range 中的 ary2。

javascript

  • .filter(d, i, arr):將原本陣列中符合條件的值回傳成新陣列,文件
    • d 為每次要處理的該筆資料
    • i 為該筆資料的索引值
    • arr 為原始的整串陣列
  • .map():把原本陣列中每個物件再處理後回傳組成新陣列。
  • .reduce():將一組陣列資料經由累加器,利用回呼函式,再處理成為單一值,文件

跟著老闆開始動手做

D3 是資料導向的工具,會針對一筆資料去做事情,也可以將 D3 理解成是一個工具,能對一組資料用同樣的顯示方法去展示資料,所以最後的程式的流程會是,將處理後的一周行事曆陣列資料,分成七天的群組資料渲染,再針對每一天的工作內容進行渲染,就能顯示視覺化的資料。

處理資料前,老闆先帶大家將 D3 這把槌子準備好,了解基本操作後,只要將資料填入,就能跑出我們要的結果。

D3 小試身手

進入製作前,老闆先帶大家畫出七個方塊,來熟悉 D3 的操作。

產出陣列 1~7

透過 D3 的 API 我們能夠快速產出一組陣列資料,如果使用 javascript 會寫了一大段 code。

// D3
var dataD3 = d3.range(1,8)
console.log(dataD3) // [1,2,3,4,5,6,7]

// javascript
var data = Array.from({length: 7}, function (d, i) {
  return i + 1
})
console.log(data) // [1,2,3,4,5,6,7]

// ES6
var dataES6 = Array.from({length: 7}, (d, i) =>i + 1)
console.log(dataES6) // [1,2,3,4,5,6,7]

資料與畫面綁定邏輯

在繪製畫面之前,讓我們先了解 D3 資料繪製到畫面的操作邏輯,假設今天有多筆資料,想要綁定到畫面中 class=”target” 的 div 上,第一步驟會先取得畫面符合規則的 DOM,接著會出現以下三種狀況與處理方式:

  • 指定 div 數量與資料數量相同:直接使用 update() 更新畫面
  • 指定 div 不存在或數量不夠:將未放入畫面的資料利用 enter() 存著,再使用 .append() 增加不足的 tag
  • 指定 div 數量多於資料數量:將多餘的 DOM 利用 .exit() 存著,使用 remove() 移除多餘的 DOM

產出 svg 圖表

有了資料後,我們也要產出畫面來將資料顯示,這邊要提醒大家 D3 是動態屬性,不是使用 css 改變外觀,而是使用 attr 來改變屬性。我們先存一個變數 svg 作為待會繪製圖表的位置,使用 api 選到 body,插入一個 DOM svg,將這個 DOM 的寬高皆設定成 700,畫面上看不到,這時去檢查開發者工具,會發現跑出一個新的 svg tag。

var data = d3.range(1,8)
var svg = d3.select("body")
            .append("svg")
            .attr("width", 700)
            .attr("height", 700)

有了資料和放這些畫面的位置(var = svg)後,我們來試著將前面產出的七筆資料放到畫面中,首先選擇畫面全部的 rect 且含有 class=”num” 的 tag,將前面產出的 data 使用 .data() 這個 api 插入畫面中。由於 svg 中目前完全沒有符合的 tag 來展示這些資料,我們使用 .enter() 定義這些新資料,並透過 .append() 來增加不足數量的 rect,最後賦予這些 tag 動態屬性。

其中比較特別的是 .attr(“x”, (d, i) => d*100 ),傳入的 function 第一個參數 d 是這筆資料內容,第二個參數 i 是這個參數的索引值。例如以陣列 [ 1, 2, 3 ] 來說,第一個傳入的 (d, i) 參數值分別是 d = 1,i =0。大家也可以將這些值 print 出來了解,譬如在這邊回傳 d * 50 作為 x 的值,就會做出七個水平間距為 50 的方塊了。

影片中老闆是使用 d * 100,由於一開始的 svg 寬度設定只有 700 ,會導致後面的方塊無法顯示,大家在實作上不用擔心。

svg.selectAll("rect.num")
  .data(data)
  .enter().append("rect")
  .attr("width", 50)
  .attr("height", 50)
  .attr("x", (d, i) => d * 100 ) // 與左上角的距離與方塊間距
  .attr("fill", "transparent") // 填色
  .attr("stroke", "black") // 框線
產出SVG七方塊
產出SVG七方塊

處理重複的標籤

我們來製作一整天幾種不同的工作項目,利用這個目標來練習 js 中的 filter 與 map,後面會頻繁地使用到這兩個功能。

我們可以拿前面老闆提供的 raw_data 來加工,首先只需要 raw_data 中做了什麼事情,什麼時間點做的對這個需求來說不重要。透過 map 會回傳新的陣列,其中的條件我們只要改成回傳 thing 即可。map 裡面的 function 為 ES6 的寫法,傳入參數為 o,為當下正在處裡的單筆資料,每次都會回傳 o.thing,所以會產出一個只有工作項目的陣列。

接著利用 filter 來篩掉重複的項目,filter 也會利用傳入的 function 作為規則,回傳一個新的陣列,傳入的三個參數分別為:

  • d : 目前處理的單筆資料
  • i :該資料的 index 值
  • arr:原始的陣列

回傳的條件為arr.indexOf(d) === i,indexOf 會從該陣列中找到第一個符合傳入條件的 index 值,所以當每筆資料在原始陣列搜尋時,和他自己的 index 值一樣,表示它是第一個出現的獨特值。利用這兩個 api 就能獲得所有工作的單一標籤。大家如果不清楚原因,可以在回傳的條件 return 前,console.log 將值 print 出來。

var new_data = raw_data.map((o) => o.thing )
                       .filter((d, i, arr) => arr.indexOf(d) === i)

相同工作的片段時間組合在一起

接下來要處理重複工作的時間段,將相同工作項目的連續時間段再處理成一個一個的群組。我們會使用到 reduce 將一天的行程處理成塊狀的陣列。首先讓我們了解 reduce 傳入的參數以及用法:

.reduce(回傳函式(累加器, 傳入的值, 傳入值的 index, 初始陣列), 初始值)

reduce 能將一個累加器及 array 中的每一個元素,透過回傳函式化為單一值。從下面的程式碼可以知道,我們將一個原始陣列,透過 reduce(),每次傳入兩個值,一開始累加器沒有值,b 為 1,第二次則處理累加器 1 與 2 後為 3,以此類推。

累加器圖示說明
累加器圖示說明
const ary = [1, 2, 3, 4, 5]
const result = ary.reduce((a, b) => a+b)
console.log(result) // 15

傳入 reduce 的參數除了處理的函數外,也可以傳入第二個參數 – 累加器的初始值。跟前面不同的地方是,我們最後想獲得一個陣列,所以將累加器初始值設定為 array。先取得累加器陣列中最後一個值。使用 slice 能將原始陣列依照傳入的數值進行陣列的切割,若是傳入 -1 ,則從原始陣列中的最後一個開始切割,並產生一個新的陣列包含原始陣列的最後一個值。

接著判斷最後做的事情 last_thing 的值,如果 last_el 存在,則最後做的事就是這件事情的 thing 值,也就是 last_el.thing ,否則代表這件事情不存在,也就是這個事件是新的群組。再來該如何判斷與前面的事件不同及如何處理呢?

  • 下一個時段的工作項目與目前時段工作項目一樣,結束時間增加
  • 上一個工作項目不存在或是與目前的工作項目不一樣,新增一筆新的資料,並將它組合進累加器

工作項目不同,表示這是一個新的工作項目,我們必須將它新增到陣列中,這邊使用到 .concat() 來將 accumulator array 與新的陣列合併。如果不清楚中間的操作,可以在處理過程中使用 console.log 去了解每個步驟值的變化。

var join_data = raw_data.reduce((accumulator, el, idx)=>{
  var last_el = accumulator.slice(-1)[0]
  var last_thing = last_el ? last_el.thing : null
  if (last_thing === el.thing) { // 上一件事情與現在的事情相同,調整結束時間
    last_el.end_hour = el.time
    return accumulator
  } else { // 上一件事情與現在的事情不同或是上一件事情不存在,新增一筆新的工作資料到累加器中
    return accumulator.concat([
      {
        thing: el.thing,
        start_hour: el.time,
        end_hour: el.time
      }
    ])
  }
}, []) // 累加器初始值為陣列

畫出一天的行程

有了處理過後的資料,就能夠將資料轉視覺化。前面有提到 D3 畫圖的方式,這邊依序跟大家解釋下面的程式碼:

  • 變數設定:將常用的單位設定成變數,包含單日圖表的寬度、單位小時的高度
  • 準備 svg 畫布:宣告畫布的方式如下,在 body 新增 svg ,並設定長寬
  • 資料填入:先選擇畫面中 svg 中所有有 hour class 的 rect 元件,將資料填入後,因為資料過多, rect 數量不夠,利用 enterappend 來增加數量,並賦予 class="hour" 的屬性
  • y 的起點:每個工作項目 y 座標的起始點都不同,利用開始時間 d.start_hour乘上 hour_height 決定每個 rect 的起始位置
  • rect 的寬高:有了初始位置,我們要將這些 rect 加上寬度及高度,寬度使用我們前面設定的 day_width,高度則是用結束時間 d.end_hour 減掉起始時間d.start_hour乘上單位小時高度 hour_height 即可,要注意的是,相減之後的數值要加上 1,因為若是結束時間和起始時間一樣,表示這個工作維持了一小時
  • 填色:將時段的 rect 加上黑色邊框
var day_width = 30
var hour_height = 20

var svg = d3.select("body")
            .append("svg")
            .attr("width", 700)
            .attr("height", 700)

var hour_rect = svg.selectAll('rect.hour')
                   .data(join_data)
                   .enter().append("rect")
                     .attr("class", "hour")
                     .attr("y", (d) => d.start_hour * hour_height)
                     .attr("width", day_width )
                     .attr("height", (d) => (d.end_hour - d.start_hour + 1) * hour_height )
                     .attr("stroke", "black")

完成之後就可以看到一條全黑的圖,接下來我們要來教大家如何填加上不同顏色

不同工作項目填上不同色

老闆這邊介紹兩個產出色碼的方式給同學

  • scaleLinear:將數值轉換為視覺變量,domain 內容為這個變量的值從多少到多少,range 的內容則是使用什麼值去對應,這邊是利用顏色去對應(圖左)。
  • scaleOrdinal:一個資料對應一個值,不用使用 domain 是因為他會直接依照順序,一個一個對應到後面 range 中的顏色(圖中),超過的值再從頭算起。大家也可以使用這個工具,直接使用 D3 提供的顏色填入 range (圖右)。
var colorize1 = d3.scaleLinear().domain([0,5]).range(["#f22", "#000"])
var colorize2 = d3.scaleOrdinal().range(["#f22", "#000"])
var colorize3 = d3.scaleOrdinal().range(d3.schemePaired)

var hour_rect = svg.selectAll('rect.hour')
  .data(join_data)
  .enter().append("rect")
    .attr("class", "hour")
    .attr("y", (d) => d.start_hour * hour_height)
    .attr("width", day_width )
    .attr("height", (d) => (d.end_hour - d.start_hour + 1) * hour_height )
    .attr("stroke", "black")
    .attr("fill", (d,i) => colorize3(i)) // 將填入的顏色換成變數代入
《D3視覺化日常作息方塊圖》三種不同的填色方式圖示
《D3視覺化日常作息方塊圖》三種不同的填色方式圖示

想達成「同種工作會對應到同一個顏色」,針對這個需求,我們要使用第二種方法。還記得前面有將工作整理成唯一值的陣列 new_data 嗎?利用 indexOf 就能取得這個值在 new_data 中的 index 值,再將這個值傳進前面使用 scaleOrdinal 做出來的工具,就能填上對應的顏色。

接著要在畫面中顯示文字,用前面產出 rect 方式,改產出一樣數量的 text 的元素。將 text 放到畫面中之後,會發現第一筆文字資料消失在畫面中了,因為 svg 文字對齊的屬性與一般文字的樣式不同,只要調整 y 的定位和 sass 樣式,按照下面去操作,文字就會在時段的框框中出現,大家也能按照自己的喜好去調整字級與位置。

JavaScript

var day_width = 80 // 調整單日寬度,讓文字能夠在 rect 內
var hour_height = 20

var hour_rect = svg.selectAll('rect.hour')
  .data(join_data)
  .enter().append("rect")
    .attr("class", "hour")
    .attr("y", (d) => d.start_hour * hour_height)
    .attr("width", day_width )
    .attr("height", (d) => (d.end_hour - d.start_hour + 1) * hour_height )
    .attr("stroke", "black")
    .attr("fill", (d,i) => colorize3(new_data.indexOf(d.thing))) // 改使用 indexOf 來作為 i值傳入

var hour_text = svg.selectAll('text.hour')
  .data(join_data)
  .enter().append("text")
    .attr("class", "hour")
    .attr("y", (d) => d.start_hour * hour_height + 5)
    .attr("x", 5)
    .attr("width", day_width )
    .attr("fill","black")
    .text(d => (d.thing))

CSS(Sass)

text
  alignment-baseline: hanging
  font-size: 12px
《D3視覺化日常作息方塊圖》完成一天的作息方塊圖
《D3視覺化日常作息方塊圖》完成一天的作息方塊圖

產生七天亂數資料

前面帶大家成功跑出一天的資料了,緊接著來準備七天的資料,讓神奇的 D3 槌子敲一下,產出一週的工作視覺化資料。

如何產出一天的亂數資料呢?先列出總共有哪些工作類別,並設定最後一件事為 null 值,在 generate_day_data 中,要亂數產生一組當天的工作項目。

首先, 利用 d3.range(0,24) 可以產出一個陣列內容涵蓋 0~23,利用 map 對這個陣列做加工,if 判斷式為,如果沒有上一件事情或是當下產生的 random 值大於 0.5,就從 catas 隨機中取得一個值做為 lastThing,這會讓下一件事情有一半的機會換值。利用 Math.random()parseInt() 可以產出新的事情,此時 raw_data 可以拿掉固定值,使用這個亂數產生器來產出一天的資料。

  • Math.random() 可以產出 0~1之間的亂數
  • parseInt() 可以將含有小數點的數值轉成整數
var catas = ["工作", "睡覺", "做專案", "吃東西", "散步"]
var lastThing = null
function generate_day_data () {
  return d3.range(0, 24).map((o)=>{
      if(!lastThing || Math.random() > 0.5) {
        lastThing = catas[parseInt(Math.random()*catas.length)]
      }
      return {
        time: o,
        thing: lastThing
      }
  })
}

能夠產生一天的資料後,接著應用前面學到的內容,就能產生一週的工作項目。使用 d3.range() 產出七個值,再利用 map 對每個值去操作,把前面處理 join_data 的內容改成 reduce_time() 傳入的參數為 arr ,之後就能將 generate_day_data 的值傳入,並將這組原始的 data,改裝成有分組的資料。

function reduce_time (arr) {
  return arr.reduce((accumulator, el, idx)=>{
    var last_el = accumulator.slice(-1)[0]
    var last_thing = last_el ? last_el.thing : null
    if (last_thing === el.thing) {
      last_el.end_hour = el.time
      return accumulator
    } else {
      return accumulator.concat([
        {
          thing: el.thing,
          start_hour: el.time,
          end_hour: el.time
        }
      ])
    }
  }, [])
}

var data_week = d3.range(0,7).map( () => reduce_time(generate_day_data()) )

繪製七天的行事曆

七天的資料準備好了,只要將這些資料繪製到畫面上就完成今天的任務。這邊我們增加一個變數 day_group,來做為七天的資料放置位置。並改寫後面的 hour_recthour_text,除了將 svg 換成 day_group 外,也將傳入的資料改成 (d) ⇒ d,讓七筆資料分別填入。D3 槌子一敲,繪製完成了。

var day_group = svg.selectAll("g")
  .data(data_week)
  .enter().append("g")
  .attr("transform",(d,i) => `translate(${ i * 100 })`)

var hour_rect = day_group.selectAll('rect.hour')
  .data((d) => d) // 改使用單日的資料傳入
  .enter().append("rect")
    .attr("class", "hour")
    .attr("y", (d) => d.start_hour * hour_height)
    .attr("width", day_width )
    .attr("height", (d) => (d.end_hour - d.start_hour + 1) * hour_height )
    .attr("stroke", "black")
    .attr("fill", (d,i) => colorize3(new_data.indexOf(d.thing)))

var hour_text = day_group.selectAll('text.hour')
  .data((d) => d) // 改使用單日的資料傳入
  .enter().append("text")
    .attr("class", "hour")
    .attr("y", (d) => d.start_hour * hour_height + 5)
    .attr("x", 5)
    .attr("width", day_width )
    .attr("fill","black")
    .text(d => (d.thing))
《D3視覺化日常作息方塊圖》繪製完成七天的作息方塊圖
《D3視覺化日常作息方塊圖》繪製完成七天的作息方塊圖

老闆來結語

回顧

再附上這次範例的成果,讓大家在實作時參考,讓我們來快速回顧一次製作流程:

  1. 了解 D3 繪製方法 filter, map, reduce 的用法
  2. 處理重複標籤,作為後續亂數產生一周行事曆使用
  3. 處理單日工作事項,相同工作的片段時間組合在一起
  4. 繪製一天的行程、不同工作項目對應不同顏色、加上文字
  5. 產生七天亂數資料
  6. 繪製七天的行事曆

萬事起頭難,一個作品不可能一步到位,將最終目標拆分成不同階段任務,從熟悉工具,繪製單日的行程到最後的七天行程,最後將這些工具組裝在一起,才有我們最後的成品。這次帶大家利用 D3 來將資料視覺化,大家也可以想想,自己手邊有什麼資料可以來讓 D3 這隻槌子敲一敲,變成一目了然的視覺圖,期待大家能用這次的教學做出有趣的應用。

如果你喜歡老闆的教學,歡迎加入老闆開的課程中一起學習,順便支持一下老闆,課程會帶你看看不一樣的作品,並引導大家一步步完成作品,透過每次的賞析、實作到修正作品,讓大家覺得寫 code 不是這麼難的事情,將這個過程想像成,拿一隻比較難的畫筆在進行創作,如果有機會使用它,便能夠在網頁上做出與眾不同的創作。

動畫互動網頁程式入門(HTML/CSS/JS)以簡單例子帶你入門網站的基礎架構及開發,用素材刻出簡單有趣又美觀的網頁和動畫,享受做出獨一無二的網頁所帶來的成就感,在職場上與設計師和工程師合作無間。

打好基本的互動網頁基礎之後,可以進階動畫互動網頁特效入門(JS/CANVAS),紮實掌握JavaScript 程式與動畫基礎以及進階互動,整合應用掌控資料與顯示的Vue.js前端框架,完成具有設計感的互動網站。長達3085分鐘,超過60個精緻範例與400張的投影片以上,以及四個加碼單元vue-cli、GSAP、D3、Three.js的投影片,成為hahow上最長的課程。

此篇直播筆記由幫手 H 協助整理

墨雨設計banner

這篇文章 用D3.js製作視覺化的日常作息方塊圖(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
釀造一門結合網頁、設計、數學與特效的線上程式課程 https://creativecoding.in/2021/06/30/%e9%87%80%e9%80%a0%e4%b8%80%e9%96%80%e7%b5%90%e5%90%88%e7%b6%b2%e9%a0%81-%e8%a8%ad%e8%a8%88-%e6%95%b8%e5%ad%b8%e8%88%87%e7%89%b9%e6%95%88%e7%9a%84%e7%b7%9a%e4%b8%8a%e7%a8%8b%e5%bc%8f%e8%aa%b2%e7%a8%8b/ Wed, 30 Jun 2021 02:51:00 +0000 https://creativecoding.in/?p=925 互動藝術家吳哲宇於2018年寫下製作完第二門線上課程之後的心得,用另一個角度發掘數學的美,將課程裡循序漸進的內容以及學生會製作的各項階段性成品範例一一呈現。

這篇文章 釀造一門結合網頁、設計、數學與特效的線上程式課程 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
本篇文章撰寫於2018/09/20,互動藝術家吳哲宇寫下製作完第二門線上課程之後的心得,自教育環境背景開始爬梳,立願將數學與科學之美與設計美感結合,並將總長超過3000分鐘的課程精華在這篇文章中統整呈現。

就在不久前的2017/8–2018/8,我花了整整一年時間,在Hahow製作我的第二門線上課程,這不只是一門線上課程,而是把我對線上課程的想像再做好做滿、做爆,做到未曾有人嘗試的東西,也希望他能夠在深度、廣度上完整超過第一堂「動態互動網頁程式入門」。

而這堂「動態互動網頁特效入門」,便是一條尋找自我的旅程,深掘是什麼造就了對互動網頁跟程式的憧憬,所以在講到這堂課的製作過程之前,我想從這堂課的主題-數學以及教育開始講起。

Hahow — 動態互動網頁特效入門課程節圖
Hahow — 動態互動網頁特效入門

數千年來,數學家與科學家花了大半輩子的時間,看著宇宙的星星月亮,推算出了萬有引力跟萬物運行的軌跡,在沙地上畫著三角形與方形,推算出了畢式定理跟得到了世界上居然有不能用分數表達的數字。

數學與科學真正的美,在於用一條式子可以預測未來,擺脫非理性的判斷跟控制,透過兩千多年來,人類不斷思考世界到底是什麼鬼東西的成果,我們終於在這混亂不堪的世界中找到了秩序,藉以預測今天月亮會怎麼變化,知道了怎麼藉由電力架構邏輯運算變成電腦,更激發了人類的潛能,促進了各種藝術跟互動科技的蓬勃發展。

古代的人看著星空都在想著些什麼呢
古代的人看著星空都在想著些什麼呢

說得很美,但是從小開始,大多數的我們都很討厭數學,有時想著我學這些東西做題目到底要幹嘛

台灣的教育,讓數學在很多人的心中是學生時期的夢靨,提到便心生反感,更何況去主動接觸,於是多少人在一次又一次的小考中,執筆的手一緊,跟不上進度的失落感,壓垮了最後一根稻草,便從此放棄數學了,只好安慰自己說著:我真的做不到,我大概…一輩子不擅長這個吧!

後來的我們,在職場的某一刻察覺做遊戲用到了大量的演算法與數學,後來的我們,也許從商、從醫,也許從事設計,發現原來生活除了找錢做生意會用到加減乘除外,很多創作也能透過數學與演算法脫穎而出。

究竟是我們沒有這樣的可能性,還是因為從沒有人帶我們從有趣的角度去看見這些東西呢?

想起國高中時期就是這樣,一直上課一直考試
想起國高中時期就是這樣,一直上課一直考試

台灣教育環境中升學導向主義極為盛行,我們以分數判斷一個孩子的學習成果,老師也就針對成績盡力的填鴨提升,在我的記憶中,正課上很少老師在教學時說:

你看!比例真的很美,在建築、設計的歷史中像是黃金比例、出版頁的隔線系統設計,都有用到,也有他的歷史故事在,不只講數學,而是能綜合一些歷史、人文、心理學的角度來闡述一個學科。

也許在教自由落體的時候,直接把帶到戶外拿不同的重量的球投擲做實驗,試著劃下那些千變萬化的軌跡,讓我們親見原來,這樣看似複雜的運動可以單純用兩個方向加速度跟速度來描述,引燃與生俱來的好奇心,我們不是沒有可能性,而是沒有看見,也許順便了解下伽利略在比薩斜塔上扔的兩塊石頭確認了下落速度並非由空氣決定的驗證。

我們在乎的,大多是這題會不會考,考試會不會拿高分

畫出反彈軌跡
畫出反彈軌跡

常聽見的是考試少三分打10下,每堂課一下課就發著一張考卷接連著考,聯絡簿上寫著成績變成家長口中彼此比較的話題,今天不努力上課唸書長大你就找不到好工作,過不了好的生活,而過多的外部動機可以摧毀內部動機,讓我們失去做這些事的成就感,不再為了喜歡做這件事而開心,學習亦是如此。

「對於教育,我有著更為浪漫的想像」

雖然不切實際,我仍希望教師的角色是一盞明燈,帶著你撥開混沌的迷霧,看見世界真實的模樣,而不是拿著書本直接塞給你這東西那式子,然後就發下考卷只看分數。

最初的嘗試 — Youtube線上直播教學

2017 四月時,我嘗試經營直播「老闆,來點寇汀吧!」,以自己設計精緻的範例直播寫程式,吸引了一批熱愛技術與視覺設計的工程師跟設計師,雖然備受好評,卻在數次直播之後感到吃力,每次從基礎開始講解太久,後期講到用三角函數做特效時,在留言區大家總是一遍哀嚎。

數學好難,還是放棄來去做視覺吧,我們可能就是沒這天份

聽了當下真的是很生氣XD ,怎麼會還沒嘗試過就直接放棄了呢?於是出國前我花了一整年時間,製作了「動態互動網頁特效入門」,橫跨基礎網頁、JS入門、Canvas與特效動畫到Vue進階應用,超過60個精緻範例與400張以上的投影片的課程,貪婪地把自己的所學化為精緻的教材,不到十個小時募資超過600%,至今已接近2300位學生。

「動態互動網頁特效入門」的掙扎與誕生

這堂課最初的點子,是來自於做在直播時做canvas動態時鐘,講到了畢式定理跟一些三角函數的應用,當時真的很害怕大家會一頭霧水,還好用到的部分數學還沒很難,所以順利地把範例寫出來,也直白地做到讓大家能懂,教東西虐待大家的時候還挺有成就感的XD

Youtube直播「三角函數做科幻時鐘網頁」
當初直播做時鐘前的數學課XD 一堆人在留言崩潰(直播影片

從那之後動機越來越強烈,延伸成第二門課的主題「動態互動網頁特效入門」,也觀察到一個現象,很多前端工程師或設計師在寫動畫或是特效的時候,都是用現成的函式庫或程式碼,我想讓大家看見,怎樣從基礎了解原理,如何巧妙地去應用相關的概念或演算法。

用有趣的範例,從程式、數學、或設計,整合式的帶你走入絢麗的特效網頁世界,從解決真實的問題或創作來看見學習的目的。

從一個完整入門的角度開發各式互動網頁的經驗,敘述自己平常怎麼思考問題、如何理解程式運作,不只是會寫寫網頁語法,而是真的用這些工具,循序漸進地從自己的手中做出絢麗、夠專業的作品。

所以我從要怎麼做出特效動畫開始想起,首先用程式畫一些簡單的圖形,然後因為座標開始複雜了,就會學習如何使用向量來操作東西,接著東西要能互動就要引入物件的概念,並且賦予每個在畫面上的元素特性,最後是一些進階的數學,比如三角函數、極座標等等。

使用Trello規劃每個單元想要教的內容,然後做範例、做簡報、錄製講解跟live coding
我會像這樣用Trello規劃每個單元想要教的內容,然後做範例、做簡報、錄製講解跟live coding

我把所有想要教的概念一個個放到Trello上,頓時覺得我好像在教高中數學啊XD 很希望一教完這些概念就能讓大家看見成果,所以我每個都設計了一個很精緻的案例,為的就是讓大家一學完就有感覺,更會愛上這些抽象卻又美好的工具。

首先起頭的系列單元是「HTML/CSS 快速入門」

因為畢竟是寫網頁,所以還是需要把基礎的元素、語法以及基本的html/css 以及pug/sass講清楚,但是因為很多人是舊生,所以我重新設計了全部的範例,並加入更多漂亮的示範帶大家精煉入門。

課程單元作業成品
課程單元作業成品

課程內容包含flex 、前處理語言模組使用,重新所有的設計範例,帶你熟悉網頁基礎語法,到使用sass/pug 做出漂亮介面,鍛鍊到能夠憑著純粹CSS就能寫出很漂亮的動態效果,我最喜歡的是裡面我做很久的時間盒子,可以切換動態的流星雨與白天,全部用html與css建構成。

全部用html與css建構成的動態網頁:時間盒子
全部用html與css建構成的動態網頁:時間盒子

接著是程式基礎:「JavaScript入門到進階 」

程式語言的邏輯非常重要,如果有學過任何一種語言,應該就能體會到其實有很多基本的概念要掌握,從變數、字串、迴圈到簡單的邏輯判斷、陣列操作,到開始加入時間的控制製作動畫,我認為程式的基礎重要在於說,知道我們要解決問題,然後用有邏輯的語言敘述邏輯給電腦執行,所以不僅是了解語法,而是了解「為什麼會有這個概念,又能用在哪裡」。

JS單元投影片裡面有非常大量概念圖解
JS單元投影片裡面有非常大量概念圖解
講解setTimeout跟setInterval製作可愛的載入動畫
講解setTimeout跟setInterval製作可愛的載入動畫

教完字串操作跟動畫後,有一個Project是做摩斯密碼翻譯器,除了可以打鐵趁熱地讓大家練習怎麼操作字串跟動畫之外,更能夠完整練習函數的使用,把程式模組化、讓他們能夠重複應用。

製作摩斯密碼翻譯器練習字串操作、函數跟介面互動
製作摩斯密碼翻譯器練習字串操作、函數跟介面互動

我以十幾個有趣範例入門JS打基礎,到後面帶物件導向的概念,從寫義大利麵式的程式,到變成可以獨立完成有趣的小應用,開始加入程式之後,就有了更多能做互動的工具,最後一個完成的Project就結合鋼琴音效做了記憶遊戲,教大家遊戲控制跟流程的設計。

最後的範例-記憶方塊 還有搭一些有趣的音效應用
最後的範例-記憶方塊 還有搭一些有趣的音效應用

然後就進入主題 — Canvas與特效動畫了!

從數學座標、物理模擬、三角函數到音效設計,讓你結合數學跟程式,創造互動藝術與各種遊戲(agar.io / 小精靈 / 科幻碼表…等),做10個範例與6個大專案,這是這次課程最精華的部分XD

在製作了第一個單元的繪圖練習之後,我加上了利用偏移跟調整座標系的工具,在一個範例裡面偷渡了九個範例,快速讓大家練習進階的繪圖跟狀態保存,用堆疊的概念保存畫布當下的轉換狀態,就能夠繪製非常複雜的圖形。

練習Canvas Translate / Rotate 跟 Scale的進階繪製
練習Canvas Translate / Rotate 跟 Scale的進階繪製

熟悉了繪製之後,我們了解到畫布上面都是沒有狀態保存跟物件的概念的,所以開始要用es6的class語法製作一個球的物件,並加入滑鼠控制與偵測座標,讓我們可以去跟球互動,並學習物理概念,把位置、速度、加速度包在球的狀態中。

投影片 — 講解如何透過向量來描述加速度跟速度
投影片 — 講解如何透過向量來描述加速度跟速度
透過向量來控制球體的加速度跟速度
透過向量來控制球體的加速度跟速度

在這之後,為了更複雜的座標跟數值計算,引入了向量跟三角函數的概念,讓我們能夠更容易與直觀地去了解物件旋轉、極座標跟電腦世界中這些形狀怎麼繪製出來的過程,如果稍微延伸下剛剛的球的範例,大量的產生物件就能夠變成漂亮的粒子系統。

大量產生球體就變成漂亮的粒子系統
大量產生球體就變成漂亮的粒子系統

接著利用三角函數的概念,就能夠繪製很漂亮的時鐘,製作FUI的科幻感。

科幻時鐘的成品
科幻時鐘的成品

而到這邊,大致上就是基礎概念都教完了,最重要的就是應用了,要如何結合不同的技術建構有趣的互動範例呢?

「直接實戰,大量從頭建構的完整範例練習」

所以接下來的就是直接建構一個大型的應用範例,比如我自己喜歡的遊戲或是互動藝術,分析我想要達到的效果,然後一步一步的解析會用的原理,從頭開始建構出來。

利用機率、遞迴的概念畫出漂亮的大樹
利用機率、遞迴的概念畫出漂亮的大樹

如果結合繪製加上遞迴的概念製作生成式藝術,學會利用機率的概念長初樹枝,加上一點電腦圖學的烏龜繪圖法,就能夠畫出漂亮的大樹。

TweenMax結合速度函數,可以操縱數值做出很漂亮又生動的動畫
TweenMax結合速度函數,可以操縱數值做出很漂亮又生動的動畫

學習完靜態的操作之後,如果使用TweenMax結合速度函數,可以操縱數值做出很漂亮的動畫,所以除了直接操作html元素,還能拿來針對js的物件製作連續的平滑數值,就能夠套用動畫法則讓畫面很生動。

利用向量模型製作引力、斥力再結合物件導向,就能做出很好玩的小遊戲
利用向量模型製作引力、斥力再結合物件導向,就能做出很好玩的小遊戲

如果學完用TweenMax動畫之後利用向量模型,可以製作引力、斥力,與接觸判斷,再結合物件導向從遊戲物件繼承到每個玩家,就能做出一個有幾乎完整功能的agar.io,再加一點點小判斷就能幫其他的球加上AI,我自己做完整個範例玩了很久XDDD

可愛版的小朋友下樓梯XD
可愛版的小朋友下樓梯XD

接著結合物理的概念,製作小朋友下樓梯的遊戲,樓梯有不同的種類、物理性質,加上遊戲控制邏輯之後,使用繼承的概念製作遊戲物件到延伸成不同類型的階梯,懷舊的小遊戲就在手中誕生了。

經典的小精靈
經典的小精靈

在後期的幾個大Project中,我自己最喜歡的是做了經典的小精靈,從地圖的設計、物件生成、牆面繪製,到如何去控制小精靈跟鬼的動畫,最後加上接觸判斷、鬼的吃食物機制,和會追隨玩家的AI,兩個小時的範例,但我用了將近兩個禮拜完成。

太空侵入者 我真的很喜歡這些古早遊戲XD
太空侵入者 我真的很喜歡這些古早遊戲XD

好啦我知道真的很多,所以製作課程的時候真的很崩潰,每次都在埋怨自己當初為什麼要開這麼多單元… 但是我既然答應了大家,我就要完整地完成這個東西,不僅是對自己負責任,而是真的燃燒生命,讓大家看見我所相信的世界。

最後系列是前端資料框架的使用 — Vue.js

因為現代前端框架真的很重要,所以最後從了解為什麼需要資料同步與前端框架,帶你從應用情景(車票、商品列表、電影選擇、撲克牌遊戲)入門,掌握如何將vue.js應用在互動網頁上,也特意設計了互動範例,不只是做個簡單的todolist,而是能拿來製作實務應用中精緻的互動遊戲或介面。

猜撲克牌遊戲
猜撲克牌遊戲

這個系列單元的Project是製作一個電影清單選擇的介面,結合Vue.js先將資料渲染成網頁上的元素,然後解構Vue.js中的事件觸發,加上TweenMax調整裡面使用者動作的觸發跟回饋,在裡面練習了CSS的調整介面、FlexBox、Vue的渲染跟計算屬性、方法、製作非同步的動畫狀態…等,當初也是構思非常地久,如何練習得到每個有教過的細節。

Vue.js製作電影院
Vue.js製作電影院

加碼單元

在最後加碼單元中,我介紹了 TimelineMax / D3 / Three.js ,幾乎每一個單元都可以當成獨立的課程來教了,裡面直接用60分鐘帶大家了解函式庫的特性、使用方式,比如用timelineMax+滑鼠來製作卷動動畫,或使用Three.js製作一個酷炫的原子轉轉畫面。

使用Three.js製作一個酷炫的原子轉轉畫面
使用Three.js製作一個酷炫的原子轉轉畫面

課程內容介紹大概就到這,還有超過一半的範例並沒有放上來,但每一個都是熬了好幾天夜,白了好幾根頭髮才生出來的,好多失敗的草稿,好多覺得不夠好的範例都一再一再地雕琢,為的就是最後端上來,呈現在大家眼前的每個東西都看起來足夠完美。

「結合物理、動畫、音效、藝術、文字、技術知識、古早遊戲,最大程度打開你對互動網頁的想像」

內容的深度、廣度、範例複雜度,如果把三者全部點好點滿…

原本以為這堂課最困難的地方會是範例設計,但是隨著準備投影片的過程,發現要把這些數學跟程式的概念講解得夠清楚直白實在是很難啊!不僅要從日常有趣的範例開始帶入主題,還要一些圖解跟動畫輔助才能一目瞭然,尤其要重新消化一遍自己熟悉的東西。

講義的一部分,JS入門有300張,Canvas特效部分有300多張
講義的一部分,JS入門有300張,Canvas特效部分有300多張

錄製過程中的困難

一直以來,我希望實作可以藉由完整的思路,能從零開始建構每個範例作品,因此在錄製課程時總是花很多的時間在不斷順內容,不斷重錄,以及一直修改投影片,雖然時間上的掌控一直沒能拿適當,常會花比預期長非常多的時間在準備課程。

因為對自己的標準提升,所以錄音、口條與後製都更完美主義了,想更清楚的講解概念,準備了數百張投影片跟說明圖,同時程式變得更難、更複雜,講解難度大幅提高,因為沒有人開設過這樣的課程,得要慢慢濃縮淬煉出整條課程的大綱路線。

Agar.io單元的預覽版本,從基礎原理、應用到livecoding

這個影片大致上就是整堂課的流程,從開頭、講解投影片跟原理,然後到livecoding把整個案例實作出來,一刀未剪,完整的從零開始建構,一邊講解思考流程、為什麼這樣做,以及語法要用什麼。

對我而言,因為製課難度的大幅提升,每個單元的時間都非常難以掌握,常常因為想要設計一個很棒的範例,一兩個禮拜就過去了,再加上還要把觀念消化、講解、程式練習講過幾遍跟重構,所以其實單元上線拖了非常之久,也很感謝所有學生耐心與包容。

另外很感謝的是Hahow負責Review我課程的詩涵,完整地把3000分鐘看過(真的很厲害),給予修改跟調整的建議,以及協助我把整體的課程時程規劃做好,和在過程中處理了很多對外的客服問題,雖然知道這堂課的時程延誤了很多,仍然耐心細心地一起處理完整堂課程。

我都用ScreenFlow剪接螢幕錄影跟聲音
跟Hahow檢核用的單元製作表格
跟Hahow檢核用的單元製作表格

那些瀕臨放棄的時刻

2018三月時是最黑暗的時期,課程上線進度落後,不斷被催稿,每次錄音看天逐漸亮了就覺得好崩潰,想到要連續講3小時的範例就覺得焦慮,要繃緊神經到極限,順暢、有調理、好吸收地把整個程式重新實作一遍。

幾乎都是半夜錄音到早上,還要去上班跟趕案子,真的很崩潰
幾乎都是半夜錄音到早上,還要去上班跟趕案子,真的很崩潰

總想著,我幹嘛把東西做那麼精緻,但是現在回頭看,就好似看到茫茫大雪中,一條深刻且扎實的足跡就這樣踏了過來,唯有熬過那樣的寒冬,才能留下在心中足夠完美的這一門課,迎來百花綻放的春天。

這一年來不斷地在攝取新的領域,同時規劃完整的基礎入門,比如我在音樂學到了合成器、聲音的基礎概念,就把他直接應用到範例中,或是看到了什麼真的很酷的遊戲,就想著怎麼把他用自己的技術與想法詮釋,很希望能夠在此留下一個里程碑,把至今探索數學跟程式的整合道路,以開發軟體、遊戲跟網頁十年左右一路學習過來的脈絡呈現。

為這條艱澀的道路點上一盞一盞的路燈,從此不再漆黑

一場沒有終點的旅行

對於很多人來說,這一堂課就像是一場沒有終點的旅行,而對我來說是很大的里程碑,把從開始寫程式、開工作室到現在的功力都放在這裡,做一個完整的整理希望能幫上想要探索這條路的人,一方面也是告訴自己告一個段落,不要在已經會的東西裡頭打轉,再起步伐去探索未知的世界。

寫網頁真的只有如此嗎?

我們是否因為工作而忘記了創作的初衷?

學會數學或技術到底該怎麼用在日常生活中?

創作到底還能以什麼形式、什麼體驗展現呢?

這些疑問,想透過這堂課也充分的證明了我所相信的事物,而我也放下了至今的包袱,前往紐約攻讀碩士探詢對於這些想像的更多不同解法了。

2018/8 我前往紐約大學念數位整合媒體碩士,想要再探索更多更有趣的互動設計
2018/8 我前往紐約大學念數位整合媒體碩士,想要再探索更多更有趣的互動設計

希望不管你什麼時候回來,這門課都能成為你堅實的後盾不只教程式,更打好數學、架構設計跟特效的內功實戰,不管是剛走上程式路途的你,正在路上跌跌撞撞的,還是已經身經百戰的工程師,都能在此得到收穫。

有一天,如果有人跟我說,嘿! 我看了你的課程,謝謝你把這條足跡留下來,那一切的努力就值得了吧!

歡迎你一起加入這條通往未知的旅行

動態互動網頁特效入門,這堂課從html/css 講到 物件導向 講到 數學、音樂、遊戲的綜合應用,囊括JavaScript完整入門、es6物件導向,到後來自己了解原理並做一個遊戲引擎,範例從記憶遊戲、小精靈、生成式藝術到agar.io。

長達3085分鐘,超過60個精緻範例與400張的投影片以上,以及四個加碼單元vue-cli、GSAP、D3、Three.js的投影片,成為hahow上最長的課程。

到Hahow上課去>>>

動畫互動網頁程式入門(HTML/CSS/JS)
動畫互動網頁特效入門(JS/CANVAS)

延伸閱讀:
2016第一堂課的心得:從不只是一門線上課程-而是一場推動進化的豪賭

撰文:吳哲宇

墨雨設計banner

這篇文章 釀造一門結合網頁、設計、數學與特效的線上程式課程 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
從不只是一門線上課程,而是一場推動進化的豪賭 https://creativecoding.in/2021/06/16/%e5%be%9e%e4%b8%8d%e5%8f%aa%e6%98%af%e4%b8%80%e9%96%80%e7%b7%9a%e4%b8%8a%e8%aa%b2%e7%a8%8b%ef%bc%8c%e8%80%8c%e6%98%af%e4%b8%80%e5%a0%b4%e6%8e%a8%e5%8b%95%e9%80%b2%e5%8c%96%e7%9a%84%e8%b1%aa%e8%b3%ad/ Wed, 16 Jun 2021 03:07:00 +0000 https://creativecoding.in/?p=986 墨雨設計創辦人,也是互動藝術家的吳哲宇,於2016年第一堂線上課程「動畫互動網頁程式入門」上架後的一個月,撰寫下初衷、籌備的歷程以及實踐後的成就感,與你分享。

這篇文章 從不只是一門線上課程,而是一場推動進化的豪賭 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>

本文撰寫時間為2016/10/2,由墨雨設計創辦人,同時也是互動藝術家的吳哲宇親筆記錄下關於「動畫互動網頁程式入門」這堂線上課程的來龍去脈,從最開始的起心動念、一路的辛苦籌備路程,還有被越來越多的學生上了幾堂課後的作業所驚豔,更加堅定地相信設計跨領域程式、以及各領域的雜學所能帶給世界的創新能量,並且願意帶著後起之秀一同前進,持續推動互動領域(無論是網頁、科技、設計乃至於藝術產業)的發展。

在課程上線滿一個月的這天,啜著咖啡看到人數已逐漸來到了1667人,整理著每天到三四點睡留下來的瓶瓶罐罐跟咖啡杯,試圖留下些什麼紀錄,回神才發現準備課程這段漫長的道路已接近了尾聲,便打起了草稿想把這一切曲折離奇、燃燒生命的光芒記下

「寫網頁好難喔….每次看線上程式教學,都在講解功能沒講怎麼完成一個好看的網頁,總覺得自己做出來,離別人美美的網站就是有一段沮喪的距離」

我想,這句話是當初開始想開課的初衷,也是自己三年前開始踏入網頁領域自學的痛,總覺得無法掌控的各種超自然力量在影響網頁的排版,各種摸不清底細的屬性,總要擺兩包科學麵獻祭祈禱網頁乖乖的不要跑掉,光是用工程師思維把網站寫出來都很吃力了,更別提要如何處理視覺、文字,讓網頁不會看起來陽春的像小學電腦課做的。

三年前第一次接網頁案子
三年前第一次接網頁案子(再之前做網頁是國中flash時代了…)

那時候第一次網頁還是脫離不了所見及所得軟體,要改東西直接拖拉,用adobe animate不顧結構做出一個還算可以用的網站,心想,做網站不過就如此嘛,我幹嘛那麼辛苦去寫html跟css,尤其當那時html只知道可以放元素,css只知道改改文字顏色改改框線的時候。

波折的三年有很多奇怪的案子

經過了波折的三年,接了數十個專案的歷練跟學習下來:

  1. 從單純的html/css,開始踏入js、踏入各種svg/canvas動畫效果
  2. 一開始接觸bootstrap之後狂用,但受限於風格跟不易改動後又捨棄,那時功力還沒有辦法秒速做到bootstrap水準。
  3. 逐漸用很基本的css/Jquery 也能夠做出很漂亮精準的互動,開始不喜歡用別人的套件,用自己理解到的原理或概念重建parallax網站或互動
  4. 開始研究物件結構跟演算法製作動態跟互動的網頁,愛上優雅的物件結構跟定義,把重力、3D、生物、遊戲等等概念濃縮到網頁上。
  5. 因為要做售票系統跟會員開始入門後端php跟資料庫,到中期為了給客戶編輯自幹了一套還算不錯用的CMS,在這個時期建立了完整前後端的概念,體驗到了溝通問題也體驗到接案的窘境。
  6. 摸過laravel/ruby on rails幾個月,覺得有趣但模板一半切完板之後交給後端處理,改動的可能性就消失了,回到原本輕型框架的網頁結構。
  7. 寫了一年後發現資料在後端處理跟產生網頁真的太麻煩,觸碰到了前端框架angular.js/ Vue.js,愛上了用資料轉換網頁的樂趣,從快速變成極快速,尤其Vue.js的調性真的很簡潔大方。
  8. 開始集大成,把手邊能用的技術逐漸都熟練,開始把實踐的功能寫成一個個模組,比如自動分析列表產生音波文字,用後期分析的方式,以canvas為基礎動態產生靈活的動畫,操作聲音、數學,覺得網頁真的可能性無限。
  9. 更加熟悉資料流/前端的互動,專案在規畫的時候會更全盤地思考如何在既有的架構跟畫面下創造更多可能性,讓網頁活起來,或是有個觸動心靈的點,甚至看到有趣可以實驗的案例就直接用網站實現出來(像是示波器)。

這些年來很多有趣的案例:

生活中很多有趣的案例都可以當作網頁的素材,所以對我而言,網頁是一種呈現媒介,很有力量跟可能性的呈現媒介,第一個:他快,可以迅速的把東西/產品上架/測試實驗性的想法,第二個:他會動,比起靜態的呈現,互動感會帶給我們很大的感受升級,第三個:他跨平台,只要有瀏覽器就有網站。

其他的範例可以到 hahow課程頁面 或我的codepen觀看。

「恩..說是要分享,但是要從哪裡開始呢?」

5月時,與hahow的魏敏討論市場上網頁技術的需求之後,決定開設一門讓大家可以輕鬆入門網頁,卻又不像是一般codeacademy / 一些免費的教學,只能大概了解概念,卻沒有辦法有系統地完成一個網頁跟調整視覺。

線上課程HAHOW平台

討論完之後,終於訂出了這次課程精準的目標,說是精準其實也有點惶恐,是一個大方向,很少人嘗試過的教學方式:

「我想要用設計的角度去教程式(恩?」

因此將課程的目標定為「用illstrator的概念了解並實作網頁」,並且每一個範例是貨真價實的,帶著把一個網頁從頭刻到尾,會碰到甚麼問題/要調整哪些css/視覺的考量如何轉換成程式的架構…等,比起怎麼做,更重視 「想要達成甚麼效果」跟「為什麼要這樣做」,才不會讓東西過於枯燥。

原本的大綱規劃有十三堂課,分別講解html基礎概念到css到帶入一些js的基礎概念,想說畢竟是”入門”課程嘛,就是每一個會用到的東西大概帶到就ok了:

///原本的課程規劃
1 - html/css/js 網站結構介紹- 一棵三種語言構成的大樹
2 - 規劃架構- 寫網頁前的規劃與準備
3 - 元素定位- 基礎元素定位 上下左右對齊與大小
4 - 基礎html/css - 網頁名片
5 - 開始開發- 架構原則- 使用 class 與 id 操作元素
6 - 開始開發- Codepen操作實務- 介紹sass與jade 快速開發入門
7 - 元素定位- padding、margin、align 元素之間彼此的關係
8 - 元素定位- div布局策略、overflow 有最好的棋盤才能下最好的棋
9 - 視覺介面- css 初階視覺調整
10 - 視覺介面- 顏色、大小、一切你在Illstrator會看到的美好事物
11 - 視覺介面- 字體 小小改變就能帶來超高CP值的調整
12 - 視覺介面- Illstrator 圖像匯入與png實用
13 - 靜態專案實作-實作模仿刻一個HAHOW的課程頁面
14 - 使用者互動- css 動畫-滑鼠的點擊、進入、滑動互動
15 - 使用者互動-基礎 css animation使用 用%影格概念操作動畫
16 - JS 基礎入門 - 使用Jquery改變元件外觀
17 - JS 基礎入門 - 使用Jquery 在各種事件中觸發網頁動態改變
18 - SVG - 向量不失真的網頁圖像格式
19 - SVG 動畫 - 讓你的LOGO或圖像動起來
20 - 動態專案實作- 做一個獨特動態的個人頁面
21 - 破除障礙- 問題解決與找資源的技巧
22 - 後續發展- 兩大路線 (前端網頁佈局/ 專攻動畫與圖形處理)

「錯了,錯了,全部都錯了」

隨著湧入的人越來越多,我一開始錄的時候發現,完全不是這麼回事啊!!!

如果稀哩呼嚕一堂講了兩個小時,誰聽得懂你在做甚麼,尤其當教學的東西到一個量之後沒有任何練習範例或看得到的產出,看的人就會開始覺得無趣抽象,抓不到你想傳述的概念,更別提去練習或了解了,這種教學方式,就像是拿著一本說明書一樣對著學生在念嘛,自己看著都覺得無趣了,更別提怎麼讓別人愉悅地把東西學進去。

想了好久好久,決定來進行一次大改造,想像自己如果是平面設計師,完全沒有程式基礎的時候,會怎麼樣去了解一個東西,為什麼要這樣做那樣做呢,到底什麼樣的範例事會讓大家覺得有趣的? 什麼才能讓大家再做中學,學中獲得成就感呢?

也因此課程暴增到了九十幾堂,每一堂原本預計5~10分鐘,但也時常超過20分鐘甚至到一個小時,就為了把最完整的思考流程、設計等等脈絡帶出來,才算是好的教學,想像自己是一個完全的新手,對於網站開始要有了解,有了全面的認知之後,再開始進入開發階段,一個個把小小的工具跟技能練好實作,再一個階段一個階段地把看似不可能的專案實踐出來,在小成就感中喜悅,逐漸成長。

Project 2 品牌視覺管理-使用sass/jade管理開發
///新的課程列表
 1 - 最新公告區(2016/9/28)-點擊"講義"查看最新課程消息
 2 - 楔子-課程進行模式說明 
 3 - 楔子-網頁前後端如何運作 
 4 - 基礎前期規劃-網站專案開發流程 
 5 - 基礎前期規劃-網站主題名稱logo 
 6 - 基礎前期規劃-網站瀏覽結構樹規劃 
 7 - 基礎前期規劃-網站排版規劃與分類 
 8 - 基礎前期規劃-網站視覺配色概念與資源 
 9 - 本機環境架設 - sublime 操作與瀏覽器預覽網頁 
 10 - 本機環境架設 - 伺服器瀏覽與原理講解 
 11 - Codepen環境架設 - 平台介紹/註冊與基礎設定 
 12 - Codepen環境架設 - 實際使用sublime中快速鍵 
 13 - 基礎HTML/CSS - html中元素&排列的概念 
 14 - 基礎HTML/CSS - html中結構&層級的概念 
 15 - 基礎HTML/CSS - <project 0> 簡易版本自我介紹 
 16 - 基礎HTML/CSS - css 直接調整外觀與顏色框線 
 17 - 基礎HTML/CSS - css 分開撰寫 - class&id概念 
 18 - 基礎HTML/CSS - css 顯示方式 display block/inline 
 19 - 基礎HTML/CSS - css 元素位置 - 相對 / 絕對定位 
 20 - 基礎HTML/CSS - css 內間隔/外間隔(padding/margin) 
 21 - 基礎HTML/CSS - css 字體變化與span-在文章段落中製作變化 
 22 - 基礎HTML/CSS - <project 1> html / css 大整合-製作獨特名片網頁 
 23 - ​基礎HTML/CSS - <homework 1> 功課說明-製作自己的技能網頁 
 24 - 系統性管理開發 - html縮寫模組-Emmet 介紹 
 25 - 系統性管理開發 - html樣板語言-jade (課程主要使用) 
 26 - 系統性管理開發 - css 樣板語言-sass (課程主要使用) 
 27 - 系統性管理開發 - sass - 變數概念管理色彩&內容 
 28 - 系統性管理開發 - sass - 動態產生模組mixin概念 
 29 - 系統性管理開發 - sass - 常用mixin與累積資源 
 30 - 系統性管理開發 - html參考資料轉換jade 
 31 - 系統性管理開發 - <project 2> 實作網頁品牌視覺指導套色 
 32 - 素材前期準備 - 如何在以拉中製作icon/圖像 
 33 - 素材前期準備 - 如何輸出圖片jpeg/png/svg 
 34 - 素材前期準備 - 將圖片上傳到imgur影像網站 
 35 - 素材前期準備 - 管理網頁色彩(guideline/sass/js) 
 36 - 前端基礎動畫互動-html attr 與自定義屬性名稱 
 37 - 前端基礎動畫互動 - css 綜合定位運用 absolute / relative 
 38 - 前端基礎動畫互動 - css 加入基礎滑鼠互動 :hover / :active 
 39 - 前端基礎動畫互動 - css 動畫基礎 transition-duration/delay 
 40 - 前端基礎動畫互動 - css 製作影格動畫 animation/keyframes 
 41 - 前端基礎動畫互動 - css 動畫速度曲線 fillmode/speed curve 
 42 - 前端基礎動畫互動 - SVG - 繪製向量網頁圖片 / logo 
 43 - 前端基礎動畫互動 - SVG - 讓你的LOGO或圖像動起來 
 44 - 前端基礎動畫互動 - <project 3> 動態互動天氣盒子 
 45 - 前端進階程式js-引言 
 46 - 前端進階程式js - jquery 動態改變css外觀/ html內容 
 47 - 前端進階程式js - jquery 初階滑鼠事件 - click/hover 
 48 - 前端進階程式js - jquery 變數概念- 儲存修改與判斷 
 49 - 前端進階程式js - json 物件陣列概念 & for 遍歷 
 50 - 前端進階程式js - jquery / json 動態產生購物車清單 
 51 - 前端進階程式js - Jquery ajax - 非同步載入與應用介紹 
 52 - 前端進階程式js - Jquery ajax - 實作導入資料代辦清單 
 53 - 前端進階程式js - <project 4> 實作購物車-動態新增購買物品與結算 
 54 - RWD原理與應用-內容流與概念說明 
 55 - RWD原理與應用-指定比例尺寸% 與max-width/min-width 
 56 - RWD原理與應用-重新排版的原理-特定條件css-media query 
 57 - RWD原理與應用-實作手機版面-模仿fb帳號頁面桌面/手機板變化 
 58 - RWD原理與應用-懶人工具- bootstrap 原理與結構講解 
 59 - RWD原理與應用-bootstrap 格線系統(container/row/column)原理 
 60 - RWD原理與應用-bootstrap 欄(xs/sm/md/lg) RWD應用 
 61 - RWD原理與應用-bootstrap 應用技巧 padding型規劃/多層div 
 62 - <project 5> 實際應用範例 bootstrap-常見企業品牌網頁 
 63 - 網頁視覺設計-流程-版面規劃軟體 AI/SKETCH/紙筆 
 64 - 網頁視覺設計-流程-與頁面結構/跳轉方式規劃 
 65 - 網頁視覺設計-元素-樣式 顏色規劃 
 66 - 網頁視覺設計-元素-元素動畫 反應規劃 
 67 - 網頁視覺設計-元素-從元素建構而上的設計 
 68 - 網頁視覺設計-元素-從頁面整體切分而下的設計 
 69 - 網頁視覺設計-元素-從設計轉換程式網頁的常用技巧 
 70 - <project 6> 實際網站應用案例-Material Design風UI/UX 
 71 - 前端框架Vue.js-為什麼要用資料導向做網頁(回顧js) 
 72 - 前端框架Vue.js-將變數代入模板中 
 73 - 前端框架Vue.js-v-for 列舉元素應用 
 74 - 前端框架Vue.js-v-model 使用者輸入雙向綁定 
 75 - 前端框架Vue.js- v-if / v-show 條件渲染與資料的後處理-實作博客來 
 76 - 前端框架Vue.js- v-on / method 建立js事件 
 77 - 前端框架Vue.js-與AJAX的對接 動態載入資訊 
 78 - 前端框架Vue.js-綜合案例-使用hahow課程資料 
 79 - <project 7> 客製化待辦清單Todo List 
 80 - 購買伺服器部署/網域-伺服器原理與規格說明 
 81 - 購買伺服器部署/網域-購買linux主機與網址DNS設置 
 82 - 購買伺服器部署/網域-購買不能ssh連線的主機設置 
 83 - 購買伺服器部署/網域-安裝apache/mysql 
 84 - 購買伺服器部署/網域-使用filezilla軟體連線與上傳 
 85 - 購買伺服器部署/網域-使用sublime sftp進行同步 
 86 - <project 7> 購買伺服器部署/網域-網站上線實作 
 87 - 實作案例分析-HAHOW-練習使用Bootstrap+Vue.js建構課程頁面 
 88 - 實作案例分析-Autodesk-影片式背景一頁式網頁+導覽列 
 89 - 實作案例分析-Medium-部落格-多資料首頁 
 90 - 破除障礙- 問題解決與找資源的技巧 
 91 - 後續發展- 兩大路線 (前端網頁佈局/ 專攻動畫與圖形處理)

我知道列表看起來有點長有點可怕,但是每個單元都是5–10分鐘(後期常超過20…),一個功能一個功能帶入並詳細地分析、演示這會用在真正寫網頁的哪些地方,帶著實作一次,也因為方法、結構都是一整套下來,照著思路練習過幾次,自然就能逐漸熟悉網頁撰寫的流程。

如果以前有人這樣帶著我這樣有趣的入門網頁,應該就可以少雷的一兩年的人了吧(笑,有些自己探索了很久去蕪存菁的重點,也是在時間歷練之下理出的心法,所以我不後悔做這樣的決定,更是與有榮焉希望有朝一日如果看到設計師們漸漸地不再懼怕程式,不管是溝通、還是台灣網頁界的現況都能夠得到極大的成長。

「每個人都是在雷人中成長,磨利了現在的自己,那為什麼要讓大家再自己坎坷的走一遍?」

曾經有一個建築廠商找我,希望能夠製作一個工地管理系統,方便能夠把工人的位置/狀態回報/或是建材等等的資訊,以一個APP或是網頁的方式呈現,在建材需要的數量/報價上也能夠及時掌握數量跟回報,他們長期以來的人事都是email傳excel檔案管理,亂糟糟的毫無章法,更常漏東漏西或時間成本全砸在上面。

這是非常好的改變,但是一個晚上他打電話來,說他不想做了,因為不管怎麼做都沒有辦法防止有心人士看到系統,然後直接請工程師團隊模仿一個出來,他說:

「這樣不就自己花了錢來讓競爭對手變厲害嗎? 我幹嘛這樣做」

然後就憤憤不平地掛電話了,當下我是十分錯愕的,談好已經幾乎要開始執行的案子突然就沒了。

但是更令我沮喪的是,他們寧可用各種複雜的excel報表,有時差地傳遞資料/手動計算,而不願意試圖改良前進,即使對手也許會看到跟進這樣的制度,台灣的傳統廠商到底發生了什麼問題,為什麼當我們踩穩了一個能夠賺錢跟成長的位置之後就放棄了進化呢?

同樣的道理套用在我的課程上,有很多人跟我說:那是你的專業耶!你捨得這樣掏心掏肺地把課程設計得像蛋糕一樣容易吸收,把大量的經驗分享出去!?你不怕別人這樣學完之後就變成你的競爭對手嗎?

我們不願意進步,恐懼別人進步,因為潛意識知道自己沒在前進。

課程錄製中大量的砲灰,大概三分之一

有有趣的對手交流較勁比自己獨鬥有趣,更何況,誰說我一輩子要靠這個吃飯,難道會寫網頁賺錢之後就要死命地抱著自己僅有的一點點技能嗎?我從來都沒有放棄前進,放棄學習,也因此在課程中學到的儘管多,也僅僅是過去的我的巔峰,一轉眼又再成為遠方的背影。

雖然這陣子錄課程錄到回台北過家門而不入XD(喂,大量的不讀不回別人訊息XD 好多好多時間都砸在這個上面,還是真的很開心的,當然不是要催大家看,影片一直都會在,只希望你記得你曾經想學習,就不會忘記初衷。

「一場豪賭 一場自我進化」

台灣現況

在傳統的職場環境(台灣)中,設計師被稱作是美工,不過我想這雙方都有一點問題,一個是設計師被動地認為自己是接收工作並且直接執行美感轉換,像是一個人體Photoshop一般的存在,所以在面對不合理的修改要求、抱怨的時候會怨天尤人,心情也差了,但還是認命地改,不企圖反駁什麼,畢竟台灣社會從小到大的教育並不鼓勵我們表達想法,多得是你就照我說的做就對了的心態。

那時候做Remy Martin的APP介面設計做超久,無限的修改…

設計做到後期之後反而覺得真正重要的是「溝通」,比起美感,不管怎麼樣的風格都有它美的極限;在不同風格間,並非誰比較好,而是誰比較適合這個載體資訊。網頁的本質是溝通媒介,所以當溝通的訊息沒有成功的傳遞,不管UI再絢麗再細緻都會很遺憾地無法發揮價值。

講到溝通的隔閡,從開始接網頁相關的案子也滿三年了,持續地成長茁壯後,看到業界發生的大部分情況都是「設計師出稿、工程師刻板」,而且在中間會有很多時候無法溝通,設計師難以了解以程式方式網頁能夠做到的,工程師覺得設計師機車一直在調整小細節或出各種難搞的spec,當固守著自己領域的時候,就會出現像是丟皮球般的埋怨跟被動放棄溝通。

「設計師們,相信我,會了技術的你們根本是外掛,自產素材VI、想動態、實作細節,更可以去探索跟完成原本純視覺設計達不到的事情」

如果有著平面設計的基礎,在設計網站版面的時候,自然能比純工程師有更高的彈性,可以改變跟嘗試的事情就更多了,除了飛來飛去、動來動去的花俏UI之外,一般中規中局的版面規劃也能以平面設計的角度關注細節,自然在介面的規劃上就拉開了一段距離,更別提很多設計師身兼插畫家,可以自己產出網頁用的素材,不用辛苦上google搜尋一堆不同風格的素材之後,苦惱怎麼拼在一起了,這樣的優勢真的是犯規級別。

所以如果設計師學習了網頁程式,有人以設計師的角度去分析,從「想要讓網站視覺變得好的時候要怎麼做」去層層剖析達到目的需要的方法跟教學,就能夠以很短的速度入門、昇華,融合原有的設計既能達到一個新的境界,這樣的境界是新時代的「雜學」,讓工具成為自己的左右手,而非最終的目的。

最後關於價錢的部分,有人跟我說

「2000元你傻啦,這樣的課程去外面xx電腦之類的根本是幾萬塊在收的」

其實我並不怎麼怕,因為你不看課程永遠損失的是自己,學到的東西永遠跟自己的努力成正比的付出。

我在教學中學到的事物,經過講過一遍之後能力三級跳,基礎功更為扎實,其實也算是收穫很多,重點是現在的時代不一樣,群募的線上課程節省了講師用無限重複的時間去授課的缺點,所以這樣開2000元收一千多個學生,跟8000元收二十個學生,而且要不斷地重複一樣的事情,這樣的效果是跟可以帶來的影響力是乘倍數增加的,也是以前時代做不到的,而我也不用那麼大的時間成本重複地授課,可以鑽研自己喜愛的事物。

像是學生裡面就有很多讓我驚嘆的,才剛學會網頁就做出真的很不錯的作品:

大家看完線上課程後創作的一些有趣的作品
大家看完線上課程後創作的一些有趣的作品

他們都是動態網站而不是純平面的作品,靠程式刻出來的! 右上 左上 右下 左下

很主動會去找各種資源/方法w 作品簡歷也簡潔有力(codepen 連結
他的動態超強大的QWQQ 而且超認真(Codepen 連結
有風格在的視覺Guideline(Codepen連結

目前,已經1600人修課 ,超過1/3的人開始第一堂課,將近100人開始完成第一個Project。

如果我們把工具當學科看,只會看到無趣,就像劇本設計一樣,核心思想才是王道,希望所有買跟修這門課的同學,我都能夠幫助到你們,就像在幫助兩年前的自己一樣,不只傳授工具、更傳授了跨領域結合、我所信仰的雜學。

「我們無法一直在這片天空上飛翔,只能奮力保持著盡量不墜落」

一起進化吧,我把到目前為止的能量都放在這邊了, 如果有朝一日能夠讓即使1%的人開始踏出自己的領域,我也滿足了。 現在的各種平台盛行,很難觸及到所有人分享,但願每個進來的人,都能收穫滿滿的離開,展開新的人生道路。 

快瞧瞧哲宇老闆精心製作的線上課程動畫互動網頁程式入門(HTML/CSS/JS)

p.s.如果這門課真的有幫助到你,讓你體會到網頁學習的樂趣, 再請不吝給予評價與分享了:)

看看下一篇:釀造一門結合網頁、設計、數學與特效的線上程式課程

撰文:吳哲宇

墨雨設計banner

這篇文章 從不只是一門線上課程,而是一場推動進化的豪賭 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
來用可怕的三角函數做網頁吧! -Part 2科幻時鐘(直播筆記) https://creativecoding.in/2021/05/14/%e4%be%86%e7%94%a8%e5%8f%af%e6%80%95%e7%9a%84%e4%b8%89%e8%a7%92%e5%87%bd%e6%95%b8%e5%81%9a%e7%b6%b2%e9%a0%81%e5%90%a7-part2-%e7%a7%91%e5%b9%bb%e6%99%82%e9%90%98/ Fri, 14 May 2021 02:16:00 +0000 https://creativecoding.in/?p=757 上一篇中,老闆利用三角函數幫 DOM 做 CSS 的絕對定位,這次我們雖然也是使用三角函數,但是改使用 canvas 在網頁上繪圖,製作科幻效果的時鐘。

這篇文章 來用可怕的三角函數做網頁吧! -Part 2科幻時鐘(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
利用三角函數與 canvas 製作科幻感時鐘
利用三角函數與 canvas 製作科幻感時鐘

本文翻自[週四寫程式系列] 來用可怕的三角函數做網頁吧! – Part2直播影片,對文章內容有疑問,或是想要老闆手把手帶你飛,都可以觀看影片詳細內容。

上次老闆帶大家利用三角函數製作衛星繞月球,還沒自己動手操作的同學,可以回上一篇挑戰,複習三角函數如何結合到網頁中。雖然數學看起來很可怕,但是它卻可以協助你製作有趣的效果,而數學中的表達式在各個地方都通用,不管是製作特效、資料分析,都可以讓你優雅地描述一件事的方式。

這次要製作的科幻效果,使用 FUI 風格,FUI 設計有許多不同的全名 (fake user interface, fictional user interface, fake user interface, 虛構使用者介面),這些 f 開頭的單字,代表還不存在的人機互動介面,常見於科幻電影場景中主角操作的機器介面,或是鋼鐵人 AI 介面…等,透過數據運算組成各種元件,讓畫面看起來科技感十足。

前情提要

開始製作前,老闆這邊提供這次範例的成果網址,讓大家在實作時可以參考。

上一篇中,老闆利用三角函數幫 DOM 做 CSS 的絕對定位,這次我們雖然也是使用三角函數,但是改使用 canvas 在網頁上繪圖。首先,讓我們快速回顧上一篇中提到的,應用三角函數幫 DOM 做絕對定位,讓遊戲元件在正確位置顯示,以砲台射擊遊戲為例,假設右上方有個目標物,我們要讓射出的砲彈的角度一直更新,才能能夠射到目標物,而每段時間砲彈要前進多少距離,則透過三角函數取得每次要調整的距離 Δy, Δx。

<canvas> 是一個 HTML 元素,我們可以利用程式在這個元素上繪圖(通常是用 JavaScript)- MDN

那麼又該如何知道 Δy, Δx 是多少呢?我們知道常見的三角形(30°, 45°, 60°)各邊的比例,但是當不是這些常見的角度時,就束手無策了。這時候我們就要感謝偉大數學家們的努力,只要知道 r 和 θ,就可以將 Δy 與 Δx 用三角函數去換算成 r * sinθ 與 r * cosθ。

數學家也從不同角度的三角形中發現,sinθ 與 cosθ 是一個規律的波浪圖,這代表著 r, Δy 與 Δx 值是成比例的關係。有了這些知識,就能開始著手製作 FUI 風格的時鐘了。以上我們帶大家快速回顧,要看更詳細的三角函數解說,可以參考 Part 1 影片和文章

畫個圓形吧

畫圖前,老闆先帶大家理解畫圓的觀念如下:從三角形、四角形,慢慢到多邊形,當與中心點距離一樣的點數亮夠多,視覺上就會像是畫出一個圓。首先,我們將中心點作為基準,每一次畫出的點與 r 的距離都相同,以 x 軸上的點做為起始點,將一圈 360° 均分為三點時,可以畫出三角形(左圖),四點的時候是四角形(右圖),以此類推,當點的數量夠多的時候就趨近圓形。

該如何將前面的這段敘述用程式寫出來呢?我們可以理解成將 360° 分成特定的份數,在座標軸上右邊 0° 是起始點,逆時針旋轉增加角度,假想有一個固定單位半徑 r 的圓在這個座標軸上,我們要畫出三角形,首先先將 360° 分成三等份,從 0° 的位置先點上一點,逆時針轉 120° 畫一點,最後再逆時針旋轉 120° 畫一點,三個點互相連接後,就成為了三角形,其他多邊形都能用這種方式去畫。聰明的大家這時候就可以發現,如果這個圓上有無限多個點,連接起來之後就趨近圓形。

那麼又該如何算出下圖中點 2 的座標呢?假設今天我們要畫一個 n 邊形,先將圓平分成 n 個點後,每次增加的角度是 360°/n,第 i 個點的角度就是 i *(360° / n)。用前面提到的三角形來做檢驗,第一個點就是 1 * (360° / 3) = 120°,第二個點就是 2 * (360° / 3) = 240°,第三個點就是 3 * (360° / 3) = 360°,也就回到了原點。第二點的座標算法為:x 座標 r * cosθ,y 則是 r * sinθ,其他點的座標算法一樣。接下來,就讓大家跟著老闆開始畫圖吧。

動手做

在我們要開始動手做之前,有幾個重點:

  1. 不能使用 canvas 中畫圓的函數。
  2. 因為電腦無法解讀 ” 從中心畫線到 30° 的位置 ” 這種口語的陳述,所以我們要去換算每個點的 x 與 y 的座標,並將所需要的點連線,才能畫出我們要的圖。

畫圖

理解畫圓的思維邏輯後,先跟大家介紹網頁畫圖的技術,在網頁畫圖我們會使用 canvas 來畫圖,畫圖的方式跟網頁定義 DOM 位置的不同,需要定義每個點的位置,點相連之後畫出我們需要的圖。

開始畫圖前,大家可以先將 codepen 的開發環境以及程式碼設置成跟老闆一樣,避免操作上的出入:

  • html 使用 pug
  • css 使用 sass
  • js 區塊將 jQuery 引用進來

HTML

canvas#myCanvas

CSS(Sass)

html, body
  width: 100vw
  height: 100vh
  margin: 0
  padding: 0
  overflow: hidden
  background-color: #000
canvas
  background-color: #fff

當我們把上面程式碼輸入後,會發現 canvas 的區塊沒有撐滿整個螢幕(下圖白色部分,這邊設定成白色背景是方便大家了解 canvas 的變化,之後在第5步驟開始畫方塊時,會將 canvas 的背景色拿掉)。

該怎麼調整 canvas 畫布跟螢幕一樣呢?我們在 js 區塊中撰寫以下程式碼,步驟如下:

  1. 利用 id 取得畫布的 DOM
  2. 透過取得的 canvas 來取得繪圖元件
  3. 設定畫布的寬高
    我們希望 canvas 可以撐開整個畫面,透過 outerWidth() 以及 outerHeight() 取得視窗的寬高,並將 canvas 畫布的寬高設定成這些值
    JavaScript
// 1. 取得 canvas 畫布
var c = document.getElementById('myCanvas');
// 2. 畫布繪圖,後面開始繪圖才會使用到這個變數
var ctx = c.getContext("2d");

// 3. canvas 畫布設定
var ww = $(window).outerWidth();
var hh = $(window).outerHeight();
// 3. 將螢幕寬高指定給畫布
c.width = ww;
c.height = hh;

4. 更新畫布大小

這時候若是調整螢幕大小,也就是網頁的寬高改變,會發現畫布沒有跟著撐滿版,所以我們必須監聽畫面的 resize,當畫面縮放的時候,動態地抓取當下畫面的寬高並重新設定 canvas 畫布。所以我們將程式碼調整成:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

var ww = $(window).outerWidth();
var wh = $(window).outerHeight();

// 將重複使用的程式碼整理成 function
function getWindowSize() {
  ww = $(window).outerWidth();
  wh = $(window).outerHeight();
  c.width = ww;
  c.height = wh;
}

// 進入網頁後先做一次畫布大小調整
getWindowSize();
// 監聽畫面 resize
$(window).resize(getWindowSize);

5. 開始畫圖 – 方塊

要注意在canvas 中畫圖比較特別的是,我們要定義兩點 a 和 b 才能連成線;若是要畫出一個方塊,可以直接透過 canvas 內的 api 拉出方塊後填色,讓我們利用以下幾個語法,畫出位於 (0, 0) 寬度 50px 的正方形:

  • ctx.fillStyle = “white” 填色顏色為白色
  • ctx.beginPath() 開始繪圖
  • ctx.rect(x, y, width, height) 在座標 (x, y) 上畫一個寬 width 高 height 的矩形
  • ctx.fill() 填色
function draw(){
  ctx.fillStyle = "white";
  ctx.beginPath();
  ctx.rect(0, 0, 50, 50);
  ctx.fill();
}

draw();

6. 隨時更新畫布

我們想讓正方形隨著時間往右移動,所以多加了一個時間變數 time,讓 draw function 被呼叫的時候增加 time 的值,並往右移動,所以我們將原本座標 x 跟著時間改變,就產生出一個白色方塊一直向右移動。

var time = 0;
function draw(){
  ctx.fillStyle = "white";
  ctx.beginPath();
  ctx.rect(time, 0, 50, 50);
  ctx.fill();
  time+=1;
}

setInterval(draw, 10);

7. 清空畫布再更新

這時候我們可以發現,方塊是往右了,但是卻變成一條白色的線,那是因為每次畫上新的方塊時,畫布沒有清空。所以我們在每次要畫方塊前,都先畫一個覆蓋整個畫面的長方形,將原本的畫面蓋掉後,再更新正方形。 若是覆蓋的長方形是半透明的會發生什麼事?沒錯!正方形移動時就會產生殘影,大家可以嘗試不同數值試試看。

...
var time = 0;
function draw(){
	ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
  ctx.beginPath();
  ctx.rect(0, 0, ww , wh);
  ctx.fill();

  ctx.fillStyle = "white";
  ctx.beginPath();
  ctx.rect(time, 0, 50, 50);
  ctx.fill();
	time+=1;
}

setInterval(draw, 10);

座標軸基礎設置

透過以上操作,讓大家了解繪圖的基本操作並讓方塊順利移動後,接著準備進入正題。首先我們要先繪製座標軸,但以網頁來說,左上角為坐標(0,0),而這次專案中我們希望座標軸的中心點(0, 0) 改在正中央,所以我們需要將中心點開始移動。

  1. 使用中心點作為初始值

要達成這個目標,我們直覺地想到把方塊移到中心,所以我們多了一組變數來記錄整個螢幕的中心點,並將這個中心點加到白色方塊的初始位置上。這邊還要注意的是,當螢幕 resize 時,center 值也必須更換,總共有以下三個部分的程式碼要調整。

// 多一組變數記錄中心點
var center = {
  x: 0,
  y: 0
};

function getWindowSize() {
  ww = $(window).outerWidth();
  wh = $(window).outerHeight();
  c.width = ww;
  c.height = wh;
  // 重新計算中心點
  center.x = ww/2;
  center.y = wh/2;
}

function draw(){
  ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
  ctx.beginPath();
  ctx.rect(0, 0, ww, wh);
  ctx.fill();
  
  ctx.fillStyle = "white";
  ctx.beginPath();
  // 每次繪製方形的座標時要加上 center 值
  ctx.rect(time + center.x, 0 + center.y, 50, 50);
  ctx.fill();
  time+=1;
}

2. 更改畫布中心

如果我們每次要繪製任何東西時,都要手動加上這個 center 的值,非常地不方便,甚至還有可能遺漏它。所以這時候我們可以使用 canvas 內建的功能,直接改變畫布位置,就不用每次繪製時都要在該元件多加上偏移的座標。這邊我們將 getWindowSize 調整成:

function getWindowSize() {
  ww = $(window).outerWidth();
  wh = $(window).outerHeight();
  c.width = ww;
  c.height = wh;
  
  center.x = ww/2;
  center.y = wh/2;
  
  ctx.restore();
  // 移動畫布座標
  ctx.translate(center.x, center.y);
}

3. 調整 draw()

完成更改畫布中心之後會發現,方塊沒出現在畫面裡,因為我們除了調整中心的畫布之外,也要將方塊的初始值調整回來。大家可以試著調整 draw 中清畫布的顏色,會發現這塊清除的色塊並沒有覆蓋整個版面,也是從中心點往右下長一個方塊,所以這塊清除的畫布的位置也需要調整。

function draw(){
  // 清畫布方塊顏色
  ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
  ctx.beginPath();
  // 調整清除畫布的初始位置
  ctx.rect(-center.x, -center.y, ww , wh);
  ctx.fill();
  
  ctx.fillStyle = "white";
  ctx.beginPath();
  // 白色方塊初始值
  ctx.rect(time, 0, 50, 50);
  ctx.fill();
  time+=1;
}

座標軸

透過白色方塊了解 canvas 繪圖和畫布座標移動後,總算要進入正題。

我們首先先繪製靜態的畫面,第一個就是我們的座標軸,比較不一樣的是,剛剛都是直接畫一個形狀填色,這次要改成用點連出我們需要的線並填色。會用到的程式碼如下:

  • ctx.stokeStyle = ‘rgba(255, 255, 255, 0.05)’ 畫筆顏色
  • ctx.strokeWidth = 1 畫筆粗度
  • ctx.moveTo(x, y) 將畫筆移到 (x, y)
  • ctx.lineTo(x2, y2) 從上一個位置拉一條直線到 (x2, y2)
  • ctx.stroke() 畫圖
function draw(){
  ...
  
  // 座標軸
  ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
  ctx.lineWidth = 1;
  ctx.beginPath();
  ctx.moveTo(-ww/2, 0);
  ctx.lineTo(ww/2, 0);
  ctx.moveTo(0, -wh/2);
  ctx.lineTo(0, wh/2);
  ctx.stroke();
  
  ...
}

從方形到圓形

有了座標軸後,我們要在上面畫一個圓形,這邊老闆一樣帶大家從四角形開始理解,了解四角形後,只要畫的點增加到一定數量,繪製出來的多邊形就會趨近於圓形。

我們嘗試畫個半徑為 200 的四邊形,首先第一個點為右邊,要注意的是,網頁的座標跟大家認知上的座標 y 軸方向是相反的,往下是正數,所以第二點是下面,第三個點是左邊,逆時針旋轉,以此類推。

另外要注意的是,網頁中畫圓是用弧度來計算,一圈 360° 等於 2PI,所以我們設一個變數來把角度轉換成弧度,若是沒有用弧度繪圖,就會出現右邊圖裡的窘境。這邊大家也可以試試看,當 n 增加到一定的量,畫出的圖就會趨近於圓形。

function draw(){
  ...
  // 每個點與中心的距離
  var r = 200;
  // 角度轉換成弧度
  var deg_to_pi = Math.PI * 2 / 360 ;
  // 畫 4 個點
  var n = 4;
  ctx.beginPath();
  for(var i = 0; i<=n; i++){
    var deg = i * (360 / n) * deg_to_pi;
    ctx.lineTo(
      r * Math.cos(deg),
      r * Math.sin(deg)
    );
  }
  ctx.stroke();
  ...
}

波浪線

那要怎麼畫旋轉的波浪圓形呢?我們可以觀察到,這個波浪圓形上的每一個點,只是在做半徑的調整,所以我們可以在 r 上面做些手腳,讓它成為一個動態的半徑,就能完成波浪的圓形,至於要如何調整才能讓這個點是在一定的範圍內變動,大家是不是想到 sin 和 cos 的圖了呢?沒錯,只要利用它們和 r 做組合,就可以調整出不一樣的波浪圖。老闆這邊提示大家:

  1. 因為 sin 和 cos 的最大到最小值分別是 1, -1,所以可以多乘上一個數,來增加波浪起伏
  2. 角度變化的越快就會讓波讓越密集,所以在代入 sin 的角度中,也多乘上一個數
  3. 波浪沒有完整的接合起來,是因為沒有把角度完整走完一個圓,一個完整的圓角度是 2PI,只要再多乘上 2PI 運算即可。

大家可以嘗試看看不同數值乘線的效果,那又該怎麼讓波浪圓形動起來呢?這邊我們要在 now_r 變數中的角度部份加入會一直變動的數值,才有辦法讓半徑多加上這個 1 單位到 -1 單位的動態半徑,我們可以觀察到, time 會隨著時間一值增加,只要活用 time 就能讓波浪圓形動起來。

function draw () {
  ...
  // 波浪圓形
  var deg_to_pi = Math.PI / 360 * 2;
  // 基礎半徑值 200
  var r = 200;
  // 200 個點的多邊形
  var n = 200;
  ctx.beginPath();
  for(var i = 0; i<=n; i++){
    // 2 * sin() 增加動態半徑的 range
    // 2 * Math.PI 確保畫出完整的圓
    var now_r = r + 2 * Math.sin(2 * Math.PI *  i / 10 + time / 20);
    var deg = i * (360 / n) * deg_to_pi;
    ctx.lineTo(
      now_r * Math.cos(deg),
      now_r * Math.sin(deg)
    );
  }
  ctx.stroke();
  ...
}

刻度

畫刻度的想法又跟畫圓形不太一樣,兩者都需要使用到角度來輔助畫圖,波浪是每畫一點就改變角度,而畫刻度不一樣的地方在於,是固定角度在 r 畫上一點後,改變 r 的長度後再畫一點連起來,完成一個角度後再到下一個角度用相同方式繪製線。

以下圖為例子,假設我們想畫一個角度 θ ,從半徑 3 連到半徑 5 的線,則先將畫筆移到(3cosθ, 3sinθ),再連線到 ((3+2)*cosθ, (3+2)*sinθ)。用基本的 r 加上某個數值,成為下一個要連線的點,這樣做的好處是,如果今天我們想變成比較長的刻度,只要調整加上的值就可以達成。

我們要畫出的圖需求如下:

  1. 畫一圈半徑為 220 共 240 個刻度所繞出的圓。
  2. 每隔10個刻度,就會有一條中間長度的刻度
  3. 上中下右(也就是每隔 240/4 = 60)有一條最長長度的刻度,

總共有三種樣式的刻度,透過三元運算子,可以先將其中一段拆解出來看,活用三元運算子就能用刻度來組出不同的長度,以下面這個三元運算子解讀的方式如下,當判斷式為 true 時,會執行前者 true 的內容,否則就執行後者 false。

判斷式 ? true : false

所以「當 i 除以 10 的餘數為 0 的時候則會使用 4,否則就使用 0」的三元運算子應該這樣寫:

i % 10 == 0 ? 4 : 0

同樣的,我們也可以將透明度結合三元運算子,在畫線前,賦予畫筆顏色不同透明度。接下來就可以設定兩個點(start_r 到 end_r)與角度,記得角度要乘上前面宣告的 deg_to_pi。

萬事俱備,就能開始繪圖了,一樣使用前面提到的 beginPath(), moveTo(), lineTo(), stroke() 來進行繪製。

var r = 220;
var count = 240;
for(var i=0; i<=count; i++){
  // len 為不同刻度有不同長度的增加量
  var len = 4 + (i % 10 == 0 ? 4 : 0) + (i % 60 == 0? 8 : 0);
  var opacity = (len > 4 ? 1 : 0.5);
  var start_r = r;
  var end_r = start_r + len;
  //最基本的角度分佈
  var deg = 360*(i/count)*deg_to_pi;
  ctx.beginPath();
  ctx.moveTo(
    start_r * Math.cos(deg),
    start_r * Math.sin(deg)
  );
  ctx.lineTo(
    end_r * Math.cos(deg),
    end_r * Math.sin(deg)
  );

  // 決定畫筆樣式
  ctx.lineWidth = 1;
  ctx.strokeStyle = "rgba(255, 255, 255, "+opacity+")";

  ctx.stroke();
}

外圈

外圈的作法很簡單,與剛剛畫內圈的方式雷同,調整 r 以及減少點(count)的數量就可以達成,大家可以按照喜好嘗試調整看看。

function draw () {
  ...
  var r = 400;
  // 共畫 60 個刻度
  var count = 60;
  for (var i = 0; i <= count; i++) {
    // 上中下右刻度要不一樣,等於是每十五個刻度就要不同長
    var len = 4 + (i % 15 == 0 ? 8 : 0);
    var opacity = len > 4 ? 1 : 0.5;
    var start_r = r;
    var end_r = start_r + len;
    var deg = 360 * (i / count) * deg_to_pi;
    ctx.beginPath();
    ctx.moveTo(start_r * Math.cos(deg), start_r * Math.sin(deg));
    ctx.lineTo(end_r * Math.cos(deg), end_r * Math.sin(deg));

    ctx.lineWidth = 1;
    ctx.strokeStyle = "rgba(255, 255, 255, " + opacity + ")";
    ctx.stroke();
  }
  ...
}

秒針、分針、時針

要畫秒針、分真或時針,我們需要知道目前時間,獲得目前時間的方式會用到以下程式,我們也利用 jQuery,將時間放在畫面上,方便我們在製作時能夠參照畫面是否和我們期望的相同。

HTML

canvas#myCanvas
.time +00:00:00

CSS(Sass)

canvas
  transform: scaleY(-1)

JavaScript

var nowTime = new Date();
var sec = nowTime.getSeconds();
var min = nowTime.getMinutes();
var hour = nowTime.getHours();
  
$('.time').text('+00:'+hour+':'+min+':'+sec);

先用固定的角度來測試畫出來的圖形是否跟真實的時鐘一樣,我們傳入 45 作為角度參數後發現一些問題,跟著下面的步驟去微調就能解決:

  1. 將角度多乘上畫圓的角度:畫出來的角度不是 45 度。只要跟前面一樣,將角度轉換成畫圓要用的角度即可。
  2. 從正上方旋轉 45 度:給 45 度為什麼是指向右下呢?我們期望會從正上方作為0度開始旋轉,所以 45 度應該要指著右上方,大家還記得在上一篇有提到,網頁中的座標系 y 軸向下才是正值(左圖),老闆這邊使用偷吃步的方式,只要修改畫布將整張畫布垂直翻轉,45度指的方向就對了。
  3. 讓秒針跟著時間旋轉:緊接著,將角度位置改使用 sec 變數讓秒針動起來。對於秒針而言一圈有 60 個刻度,我們先將 0 到 60 個刻度換算成 0 到 1,再換算 0 到 360,所以就等於 sec / 60 * 360,就是秒針每秒要改變的角度。
  4. 調整旋轉方向:我們可以看到現在以逆時針旋轉,所以我們多乘以一個負值,讓旋轉的方向變成順時針轉的方向。
  5. 補上初始值:最後我們察覺到正確的秒針位置總是少了90度,這就是因為我們現在的座標系初始值 0 度是在右邊,只要將初始值的0換到正上方,這邊我們在函式轉換的角度位置中加上初始值(90度)即可。
function drawPointer(r, deg, lineWidth) {
  // 決定畫筆
  ctx.lineWidth = lineWidth;
  ctx.strokeStyle = "rgba(255, 255, 255, .5)";
	
  // 調整角度初始值與轉化成圓的角度
  var now_deg = (deg + 90) * deg_to_pi;
    
  ctx.beginPath();
  ctx.moveTo(0,0);
  ctx.lineTo(r * Math.cos(now_deg), r * Math.sin(now_deg));

  ctx.stroke();
}

// 秒針分針時針畫法
drawPointer(400, -360 * (sec / 60), 1);
drawPointer(210, -360 * (min / 60), 1);
drawPointer(150, -360 * ((hour + min / 60) / 12 ), 4);

要注意的是,時針畫法比較不一樣,我們會希望時針不是單純指在對應的數字上,還要多加上過了幾分鐘的變化,所以先將過了幾分鐘除以 60,獲得目前經過一小時中的幾分鐘後,再加上小時除以12,因為一圈只有12個刻度,就能獲得目前的度數。

結語

老闆這邊附上這次範例的成果網址,讓大家在實作時可以參考。

這邊讓我們再複習一次整個製作過程:

  1. canvas 網頁繪圖:若是要讓畫布和螢幕同樣大小,要監聽 resize 並重新調整畫布大小。
  2. canvas 動態效果:相當於在畫布上一直重新繪圖,要記得清空畫布後再畫上新東西。
  3. 調整 canvas 座標:canvas 預設左上角為 (0, 0),可以利用 ctx.translate(x, y)調整畫布的位置。
  4. 繪製座標軸。
  5. 從四角形到圓形:利用三角函數來畫出不同形狀,當點的數量足夠時,連起來就會像是圓形,要注意要將角度換算成弧度。
  6. 會動的波浪圓形:在畫圓的時候,透過三角函數來製造波浪,並結合時間就能讓波浪圓動起來。
  7. 繪製刻度:這次範例中的內圈與外圈都是用這種方是去完成,在不同角度上點出兩個點連起來就變成刻度,結合三元運算子就能在不同角度時有不同的樣式。
  8. 繪製秒針、分針與時針:類似刻度的畫法,只是這次第一個點是圓心,再將時分秒換算成角度後畫出第二點並連線。

數學裡面有許多奇怪的東西,但也感謝數學家們的努力,讓我們可以應用在遊戲或動態特效等地方。在第一篇我們用三角函數來幫 DOM 做定位,這一篇則是在 canvas 上繪圖,並適時結合三角函數,產出許多有趣的效果,各位同學挑戰完兩篇三角函數教學之後,可以回頭思考看看兩者的差異,期待大家能利用三角函數做出更多有趣的效果。

工商時間:老闆在 Hahow 有一堂課程 – 動畫網頁特效入門,裡面有一些數學的內容,誘使大家跳坑,一起去學這些恐怖的東西,老闆已經努力將課程講解得有趣點,讓大家在比較沒有壓力的狀況下學習這些數學。(笑

此篇直播筆記由幫手 H 協助整理

墨雨設計banner

訂閱 Creative Coding Taiwan 電子報:

這篇文章 來用可怕的三角函數做網頁吧! -Part 2科幻時鐘(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
來用可怕的三角函數做網頁吧! – Part 1 衛星繞月球(直播筆記) https://creativecoding.in/2021/05/10/%e4%be%86%e7%94%a8%e5%8f%af%e6%80%95%e7%9a%84%e4%b8%89%e8%a7%92%e5%87%bd%e6%95%b8%e5%81%9a%e7%b6%b2%e9%a0%81%e5%90%a7-part1-%e8%a1%9b%e6%98%9f%e7%b9%9e%e6%9c%88%e7%90%83/ Mon, 10 May 2021 01:30:00 +0000 https://creativecoding.in/?p=536 在這篇文章中,會先詳細介紹三角函數的計算並轉換為程式語言,再以三角函數的概念,使用 html (pug), css (sass) 與 js ,從零建構出衛星環繞月球的動態網頁效果。 本文翻自[週四寫程…

這篇文章 來用可怕的三角函數做網頁吧! – Part 1 衛星繞月球(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
https://imgur.com/iqS8cqe.gif

在這篇文章中,會先詳細介紹三角函數的計算並轉換為程式語言,再以三角函數的概念,使用 html (pug), css (sass) 與 js ,從零建構出衛星環繞月球的動態網頁效果。

本文翻自[週四寫程式系列] 來用可怕的三角函數做網頁吧! – Part 1,若是對文章內容有疑問,都可以觀看影片詳細內容。

前言

這次要跟大家聊聊三角函數,一聽到「三角函數」,肯定會勾起學生時期回憶,『天啊!老師在說什麼???』,我們這次準備了衛星繞月球的案例,將三角函數結合 CSS,實現衛星在圓周上的位置。也讓大家思考,如何在遊戲中使用三角函數?三角函數還能做出什麼變化?

在開始之前,先和大家分享之前經手的兩個案例 Dyverse Studio 與複雜生活節。在 Dyverse Studio (影片 4:29) 專案中,可以看到主頁裡,背景是由很多條線連結起來的,這個效果不可能手寫網頁結構去達成,直接使用圖片旋轉也欠缺真實感。要達成這些計算藝術的效果,三角函數、座標系、canvas 三者是不可或缺的工具。那這些即時繪圖有什麼好處呢?當使用者跟滑鼠互動或是調整一些參數時,網站能夠即時變化去調整顯示出來的效果,跟一般動畫寫死位移或是旋轉相比,多了更多的互動性。附錄中的 codepen 範例網址中,包括會動的海浪波型、 CD 旋轉軌跡等效果,都是利用三角函數製作出來的效果。由於 Dyverse Studio 主頁已改版,這邊提供影片中提到的畫面連結

Dyverse Studio主頁動畫

從遊戲開始出發

開始製作衛星繞地球前,我們要先了解三角函數。在網頁中要搖操控遊戲中的小精靈移動,就會使用 CSS 的 left, top 屬性,要往右走 left 增加,要往上走 top 減少,以此類推,隨著時間增加,移動的位置也跟著改變,就能營造出小精靈在移動的畫面。

這時問題來了,若是今天我們要讓小精靈斜上(右上45°)移動,該怎麼做呢?相信大家一定非常聰明可以想到,只要在同一時間往上和往右移動相同距離就可以了。

問題又來了,如果要改成往右上30°移動呢?右邊的距離跟上面的距離比例分別為1和√3(學生時期數學課的景象慢慢出現在腦海裡…),先不要恐慌這些數字怎麼來的,只要知道要往右上角30°的目標物移動,x 座標每次都加上√3單位,y 每次都往上加上1單位。

若是想要操作物體移動速度的快慢,只要調整每次加上單位的量(10 * 1 和 10 * √3),就能決定綠色球跑得快或慢。但是這樣的換算方式並不夠理想,每次我們要調整速度,就得再去算固定時間往上或往右移動的量。

版本1:知道 x 及 y 的變量,求得角色的下一個位置。

適用於目標位置不改變。

我們可以發現,在30°橫向距離與直向距離的比值不變,都是 1/√3 ( Δy / Δx, Δ, delta 變動值 ),在比值不變的狀態下,我們只要知道 x 或 y 的其中一邊,就能知道另一者。

x+=Δx
y+=Δy

版本2:知道 x 或 y 其中一者,求得角色的下一個位置。

適用於目標位置不改變。

既然我們知道 x 和 y 成比例關係,那我們就能將 y 每次增加的變量換算成Δx * 比例,反之我們也能將 x 每次增加的變量換算成 Δy * 比例。

x+=Δx
y+=(Δx*比例)

以上兩種版本都是在目標物在特定角度下,我們能夠計算出本體抵達目標物的移動方式,但是如果有更多角度的需求時,只有三種角度的比例肯定就不敷使用了。

版本3:知道斜邊 r 之後,r 乘上對應比例,就可以找到 x, y

這時我們就要感謝數學家們的努力,發明了三角函數,只要我們知道本體和目標物的距離 r 與角度 θ,就能利用 sinθ 或 cosθ 知道對應邊的長度了。

那麼 sinθ 的值怎麼來的呢?假設今天有一個座標軸,上面有一個半徑為1單位的圓,我們將不同角度的三角形放進去,讓所有三角形的 r 都剛好等於半徑1單位,就可以求出 sinθ ( r / y ),例如45°的時候,sin45°=1/√2。

有了這些,我們就可以拿本體到目標位置的距離乘上比例( sinθ ) ,來求出 y。同樣地要求出 x,我們就可以改使用 cosθ。但是這個比例的值到底是什麼呢?讓我們來看看第四個版本

版本4:知道 θ ,就能使用 r 結合三角函數作為比例

比例會根據θ而改變

// r = 5
x+=(5*比例)
y+=(5*比例)

// sinθ = y/r
// cosθ = x/r

x += r * cosθ
y += r * sinθ

衛星繞月球

有了這些三角函數的基本概念後,我們來嘗試做衛星繞月球的畫面,但為了強迫自己使用三角函數,我們要限制自己不能使用 css rotate 的屬性,只能利用 top, left (與畫面上方與左方的距離)結合 transform 來達成。

🔔關於 rotate : css 中有個屬性 transform,裡面有許多種值可以選擇,例如: translate, rotate, skew,其中的 rotate 則是以該物件中心為旋轉基準,根據使用者填入的值做旋轉。了解更多

這邊要注意的是,一般來說,旋轉的角度為逆時針,右邊水平線為 0°,順時針旋轉增加角度(圖左),而網頁座標中旋轉的角度與大家的想像不一樣,這邊在衛星旋轉的部分會透過操作詳細敘述。右圖中可以看到,只要知道綠色衛星與月球中心的距離 r 與 θ,就能利用三角函數換算出 y 與 x 座標。

我們這次用 codepen 來製作這個作品,環境調整為 pug 與 sass,會使用到 jq 來快速選擇兩顆衛星。這邊提供大家一個選顏色的工具 colorhunt ,沒有配色靈感時,能夠直接使用別人推薦的色碼。

場景 html 結構

在畫面中分別有星球背後的光暈(.space)、月亮、月亮上的四個坑洞、兩顆衛星,因此我們整個 html 結構可以寫成下面這樣

.space
.moon
  .hole
  .hole
  .hole
  .hole
.yellow
.blue

Sass 重複使用 – @mixin

首先我們先賦予場景基本的屬性,* 的存在是為了讓我們了解每個元件的外框,在完成作品後可以刪除這個 class 內容。

$color_space: ##2c3d4f

*
  border: solid 1px

html, body
  width: 100%
  height: 100%
  padding: 0
  margin: 0
  background-color: $color_space

// 背景光暈
.space
  width: 700px
  height: 700px
  border-radius: 50%
  background-color: lighten($color_space, 10)
  filter: blur(50px)
  position: absolute
  left: 50%
  top: 50%
  // 偏移處理
  transform: translate(-50%, -50%)

.moon
  background-color: #fff
  width: 200px
  height: 200px
  border-radius: 50%

我們可以發現畫面裡的月球、衛星、坑洞,有太多重複需要使用到圓的東西,我們來試試看怎麼將這些屬性整理在一起,讓這些屬性可以不用一直重複撰寫。

我們會發現 .moon 的寬高與圓角是構成圓形的三個屬性,要如何做才能重複使用這些 css 呢? sass 內有個工具叫做 mixin,可以傳入變數進去,在編譯階段就能產出我們需要的 css 內容,這種方式讓我們能減少撰寫重複的程式碼。宣告與使用的方法如下:

@mixin size($w, $h)
  width: $w
  height: $h

@mixin circle($r)
  +size($r, $r)
  border-radius: 50%

.moon
  background-color: #fff
  +circle(50px)

如果想偷懶一下,讓 +size 內只需傳入一個 $r,可以將 @mixin size 中加入判斷式改寫成如下,在 mixin size 中我們可以看到,當有傳入 $h 時, height 就是使用傳入的第二個參數,若是沒有則直接使用 $w 作為 height;寫法2中則是當 $h 參數沒有傳入時,則預設 $h 為 $w

// 寫法1
@mixin size($w, $h:false)
  width: $w	
  @if ($h)
    height: $h
  @else
    height: $w
// 寫法2
@mixin size($w, $h:$w)
  width: $w	
  height: $h

@mixin circle($r)
  +size($r)
  border-radius: 50%

.moon
  background-color: #fff
  +circle(50px)

這時候我們又發現,專案中頻繁地使用到絕對定位並水平垂直置中,理所當然也可以把這些屬性整理成 mixin:

@mixin ab_center
  position: absolute
  left: 50%
  top: 50%
  transform: translate(-50%, -50%)

製作陰影

我們的月球、月球坑洞與衛星都會用到陰影,css 中的 box-shadow 陰影預設是往外長,我們這邊可以多下一個 inset 值,讓陰影變成內陰影,改成 -20px 便是將亮面往左上移動,知道這個方式之後,讓我們試著做做看衛星與月亮坑洞的陰影,可以試著調整陰影顏色或是偏移量。

.moon
  background-color: #fff
  +circle(400px)
  +ab_center
  box-shadow: -20px -20px darken(#fff, 10) inset

月亮不同坑洞

月亮上四個坑洞的 classname 都是 hole ,我們該如何去客製這四個一模一樣的坑洞,讓它們在基本的屬性上再增加不同的位置或是大小?這邊我們使用到 css 的類別選擇器 nth-child,不僅位置可以客製,每個坑洞的大小也可以透過這種方式去調整。

.hole
  &:nth-child(1)
    left: 120px
    top: 130px

不知道大家在分別寫四個坑洞的位置時,有沒有查覺到我們也可以用剛剛的 mixin 去寫呢?同學可以挑戰看看。

@mixin pos($left, $top)
  top: $top
  left: $left

衛星軌道

這一小節中我們要製作衛星軌道,對好位置後,也能夠確認衛星沒有偏離,這邊只要增加 html 結構,並為它們賦予 css 即可,這邊我們只示範 .trace1 的寫法,要特別注意的就是衛星旋轉的圓型軌跡直徑,就是這個軌道寬度和高度:

HTML

.trace1
.trace2

CSS

.trace1
  width: 500px
  height: 500px
  border-radius: 50%
  border: 1px dashed #fff
  +ab_center

旋轉的衛星

幫衛星賦予樣式,在這邊我們想讓衛星有內陰影,以及淺淺的外層黑色光暈。所以我們在 .red 和 .yellow 的 css 上,加上一些程式碼。這邊也可以看到,我們將常用的顏色做成變數,方便之後快速調整,box-shadow 前面的部分是內陰影,逗號後面的則是外層黑色光暈,大家在寫這一段也要記得加上 z-index

這邊提供大家 .red 的 sass 檔,黃色衛星的內容大家可以挑戰看看。

.red
  $color_red: #f24
  background-color: $color_red
  +circle(50px)
  +ab_center
  box-shadow: -10px -10px darken($color_red, 20) inset, 0px 0px 40px rgba(black, 0.3)
  z-index: 100

接著我們要著手撰寫程式碼,讓程式碼動態修改角度,使衛星們順利動起來。

結合前面所解說的方式,利用三角函數來定義旋轉的位置。angle 為旋轉角度,這邊先釐清前面提到的旋轉方向,一般來說水平線右邊為0°,角度增加的方向為逆時針(左圖);但在網頁中 x, y 的方向有所不同,y 向下才是正值,角度增加旋轉的方向為順時針(右圖)。

我們先做一些測試,確定網頁中旋轉的角度是不是如右圖所示。

下面這段程式碼,angle 是我們要觀察的角度變數,r 為紅色衛星的半徑,x 的部份我們有提到要使用三角函數的 cosθ , y 則是使用 sinθ,那為什麼 θ 的值會使用到這麼大串運算式呢?這邊我們先一一理解整段程式碼,javascript 內要使用到 cos 要使用 API – Math.cos(),javascript 內角度不是直接寫數字,要換算成Math.PI,一圈 360° 為 2PI,所以我們將角度除以 360 後乘上 2PI,就能換算成 js 內的角度。

而最後的 – 25 則是偏移量,因為我們在 mixin ab_center 內有寫到translate(-50%, -50%),這邊我們要將這個偏移量修正回來,才不會讓衛星旋轉偏移。

var angle = 0
var r = 250
var x = r * Math.cos((angle/360)*(Math.PI*2))-25
var y = r * Math.sin((angle/360)*(Math.PI*2))-25
$(".red").css("transform", "translate("+x+"px, "+y+"px)")

大家可以慢慢增加 angle 的量,就能發現紅色衛星隨著角度的增加,從右邊水平線順時針轉。

https://imgur.com/p649GNo.gif

接著我們要讓衛星順暢的旋轉,這邊使用到 setInterval。我們先將剛剛的程式碼包裝成 function update,每隔一段時間就增加 time 的量,並更新畫面就能讓角度增加,使用 setInterval 每 30 毫秒呼叫一次,一個順暢的動態就產出了。

若是覺得衛星轉太慢或太快想調整衛星速度,我們只要把 var angle = time 多乘上一個值即可,聰明的大家應該有發現,如果我們乘上的值是負值,就輕鬆的達成反方向旋轉的功能了。

var time = 0
function update(){
  var r = 250
  // var angle = time
  // var angle = time * 0.2
  var angle = time * -0.5
  var x = r * Math.cos((angle/360)*(Math.PI*2))-25
  var y = r * Math.sin((angle/360)*(Math.PI*2))-25
  $(".red").css("transform", "translate("+x+"px, "+y+"px)")
  time+=1
}

setInterval(update, 30)
https://imgur.com/TGkqKLj.gif

這時候問題來了,紅色衛星完成了,黃色衛星的程式碼也類似紅色程式碼,差別只在旋轉半徑、速度、偏移修正不同,那我們要怎麼將它統一呢?這邊我們使用陣列去管理這兩個衛星物件,在 update 內使用 forEach 來修改兩個衛星的位置就可以了。

var time = 0
var stars=[
  {
    el: ".red",
    r: 250,
    speed: -2,
    width: 50
  },
  {
    el: ".yellow",
    r: 340,
    speed: 1,
    width: 70
  }
]

function update(){
  stars.forEach(function(star){
    var r = star.r
    var angle = time * star.speed
    var x = r * Math.cos((angle/360)*(Math.PI*2))-star.width/2
    var y = r * Math.sin((angle/360)*(Math.PI*2))-star.width/2
    $(star.el).css("transform", "translate("+x+"px, "+y+"px)")
  })
  time+=1
}

setInterval(update, 30)
https://imgur.com/iqS8cqe.gif

結語

這邊讓我們再複習一次整個製作過程

  1. 開始製作前,我們先將基本的結構與樣式完成(陰影、坑洞等),過程中我們發現許多重複使用的樣式,例如絕對定位、圓形等,所以我們學到了第一招 sass 中的 mixin。
  2. 我們利用三角函數模擬衛星繞星球旋轉的定位,並確定衛星旋轉的方向後,讓紅色衛星順利轉起來。
  3. 最後我們將旋轉兩個衛星整理成物件,並改寫 update 函式,讓兩顆衛星能共用相同函式旋轉。

數學裡面有許多奇怪的東西,但也感謝數學家們的努力,讓我們可以應用在遊戲或動態特效等地方,即使理解這些數學理論有些頭疼,但是一切都是為了做遊戲和特效。在 part 2 裡我們會再帶大家使用三角函數來製作其他有趣的東西。

下一篇老闆要繼續使用三角函數打造一個科技感的時鐘,讓我們一鼓作氣學習下去吧!

工商時間:老闆在 Hahow 有一堂課程 – 動畫網頁特效入門,裡面有一些數學的內容,誘使大家跳坑,一起去學這些恐怖的東西,老闆會將課程講解得有趣點,讓大家在比較沒有壓力的狀況下學習這些數學。(笑

此篇直播筆記由幫手 H 協助整理

墨雨設計banner

訂閱 Creative Coding Taiwan 電子報:

這篇文章 來用可怕的三角函數做網頁吧! – Part 1 衛星繞月球(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
JS及網頁的模板好夥伴:Canvas sketch入門 https://creativecoding.in/2021/04/30/js-canvas-sketch/ Fri, 30 Apr 2021 01:46:00 +0000 https://creativecoding.in/?p=673 哈嚕,我是陳柏文,目前在陽明交通大學人工智慧辦公室擔任全端(主要是前端),語音及影像辨識工程師,因為興趣使然,正努力學習一些新知,所以目前也在墨雨設計擔任小小前端實習生。 Canvas sketch …

這篇文章 JS及網頁的模板好夥伴:Canvas sketch入門 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
哈嚕,我是陳柏文,目前在陽明交通大學人工智慧辦公室擔任全端(主要是前端),語音及影像辨識工程師,因為興趣使然,正努力學習一些新知,所以目前也在墨雨設計擔任小小前端實習生。

Canvas sketch

今天我要來介紹canvas sketch這個工具,作者是來自英國倫敦的Matt DesLauriers,他是一名creative coding的前端工程師,也是國外知名教學網站FrontendMasters的講師。

為什麼要學習canvas sketch?

許多前端工程師在做creative coding時,往往會不知道該如何起草或者無法統一,在開發上就需要重新造輪子,因為沒有一個統一的模板;而canvas sketch提供了非常好的模板,像是p5, three, webgl, glsl, shader…等,及一些在創作時會時常使用到的便利工具集,你也可以輕鬆使用canvas sketch方便的快捷鍵將創作好的作品輸出成高畫質的png,或著連續輸出多張照片,並運用工具集,創作成gif或mp4…等的檔案,大大提升你創作的便利性及多元性。廢話不多說,先教大家如何用一行程式碼即完成安裝。

Github連結:https://github.com/mattdesl/canvas-sketch

# 將canvas-sketch-cli全域性的安裝,細節的部分可以參考下方github的連結
# https://github.com/mattdesl/canvas-sketch/blob/master/docs/cli.md
npm install canvas-sketch-cli -g

# 接著可以使用以下兩種方式確定是否有安裝成功
canvas-sketch --version # 檢查版本
npm list -g --depth=0 # 檢查npm global安裝了什麼package

安裝完後接著該如何執行呢?

# 運行下方程式碼,即可產生sketch.js的檔案
# 原作者也有提供像是p5.js,webgl,three,glsl...的模板,可供選擇,範例: --template=p5
# Tips: 如果檔案已經存在,可以省略--new
canvas-sketch sketch --new 

下方是產生three的範例,是不是很乾淨俐落呢?

Canvas sketch產生three的範本
Canvas sketch產生three的範本

以Canvas sketch做圖片輸出

在做creative coding時往往前端工程師或是設計師會希望可以將圖片輸出,而canvas-sketch如果點擊畫布並按下 cmd/ctrl+k 便可簡單地將圖片輸出,並可透過程式進行設定(會在下方圖片設定解釋如何使用),或是,如果你習慣使用git,也可以使用 cmd/ctrl+k ,除輸出外,還會將程式碼進行commit,如下圖。

Canvas sketch輸出圖片

如果需要製作gif檔或是mp4檔可以使用 cmd/ctrl+shift+s ,算法為fps*duration,拿我的例子來舉例:4*24 所以總共會輸出96張圖(這邊要注意:如果未設定duration程式會一直輸出,如果不小心遇到,請關掉瀏覽器),接著如果有安裝ffmpeg便可將圖片串接起來,或著使用網路上的工具,這邊我會介紹如何安裝ffmpeg。(如果有使用homebrew也可使用brew安裝,windows請使用choco)

# 安裝ffmpeg
npm install @ffmpeg-installer/ffmpeg --global

# 安裝完成後便可使用--stream 輸出成mp4
canvas-sketch animation.js --output=tmp --stream

# 輸出成gif
canvas-sketch animation.js --output=tmp --stream=gif

# 輸出成gif 但將長寬度改為512
canvas-sketch animation.js --output=tmp --stream [ gif --scale=512:-1 ]
Canvas sketch製作gif檔或是mp4檔
https://imgur.com/RjICB48.gif

圖片輸出的一般設定

下面提供一些輸出的一般設定,如果需要更進階設定選項可以至github上面查詢,此外也可以將圖片透過像是blender, unity的軟體產生更有趣效果唷。

JavaScript

const canvasSketch = require('canvas-sketch');

const settings = {
  dimensions: [ 2048, 2048 ],
  // dimensions: "A4",         // 將圖片輸出成 A4 size
  // orientation: "landscape", // landscape, portrait (背景、人像)
  // units: "cm",              // 預設是px,可更改輸出的單位
  // pixelsPerInch: 300,       // 預設為72,可以輸出更高畫質的圖片 
	// fps: 24                   // 每秒輸出多少張
  // duration: 4               // 輸出長度
  // name: 'foobar',           // 預設為時間time stamp
  // prefix: 'artwork',        // 檔案的前綴
  // suffix: '.draft'          // 輸出檔案檔名
	// encoding: 'image/jpeg',   // 輸出類型
  // encodingQuality: 0.75,    // 輸出壓縮品質
};

const sketch = () => {
  return ({ context, width, height }) => {
    context.fillStyle = 'white';
    context.fillRect(0, 0, width, height);
  };
};

canvasSketch(sketch, settings);

Canvas sketch所提供其他好用的工具

為了方便開發,canvas-sketch的作者也有提供一些好用的工具,像是random,以往我們使用javascript原生的Math.random 每次刷新後都會產生不同的值這裡便可以使用作者製作的random…等的函示。

Github連結:https://github.com/mattdesl/canvas-sketch-util

當然除了作者提供的canvas-sketch-util,還有其他不錯的library像是nice-color-palettes … 等的套件可供使用。

# 安裝canvas-sketch-util
npm install canvas-sketch-util --save

JavaScript

// 隨機數
const random = require("canvas-sketch-util/random");

random.setSeed(1); // 設定後1的值便會存起來
random.value();    // 隨機值
random.gaussian(); // 可以使用類似gussian的函示
random.noise2D();  // 產生兩層的雜質
random.noise3D();  // 產生三層的雜質

// 數學函示
const { fract, lerp } = require('canvas-sketch-util/math');

fract(51.23)       // 扣除整數,得到0.23的值
lerp(0, 50, 0.5)   // 線性插值,0跟50取中間值,所以為25

總結

寫程式寫到現在,在琢磨的過程中,發現前端有很多未知的新東西需要更多的工程師去一起去挖掘及開發,儘管有些難度很高,但越過去後總是能看見另一片天地,而我想這也是每個工程師能一直保持熱忱的主要原因;今天簡單介紹完canvas-sketch這個工具,希望對身為工程師或設計師的你們在開發上能有所幫助,未來如果有新的知識,我也會繼續分享,若是你們有甚麼想知道的,也歡迎透過社群平台告訴我。 ; – )

作者:陳柏文

墨雨設計banner

訂閱 Creative Coding Taiwan 電子報:

這篇文章 JS及網頁的模板好夥伴:Canvas sketch入門 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
讓我們來快速寫個漂浮感的輪播吧!動態網頁程式教學(直播筆記) https://creativecoding.in/2021/04/26/%e8%ae%93%e6%88%91%e5%80%91%e4%be%86%e5%bf%ab%e9%80%9f%e5%af%ab%e5%80%8b%e6%bc%82%e6%b5%ae%e6%84%9f%e7%9a%84%e8%bc%aa%e6%92%ad%e5%90%a7%ef%bc%81%ef%bc%88%e7%9b%b4%e6%92%ad%e7%ad%86%e8%a8%98%ef%bc%89/ Mon, 26 Apr 2021 05:36:00 +0000 https://creativecoding.in/?p=626 相信大家對於輪播一定不陌生吧,在各大官網、購物網站的重點區域常常可以看到輪播的蹤跡。除了增加與使用者的互動性以外,對於尺寸日漸縮小的行動裝置來說,輪播很重要的一點是可以在有限版面中增加傳遞的資訊量。今…

這篇文章 讓我們來快速寫個漂浮感的輪播吧!動態網頁程式教學(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
漂浮感輪播教學成果

相信大家對於輪播一定不陌生吧,在各大官網、購物網站的重點區域常常可以看到輪播的蹤跡。除了增加與使用者的互動性以外,對於尺寸日漸縮小的行動裝置來說,輪播很重要的一點是可以在有限版面中增加傳遞的資訊量。今天我們就來實作如何透過Vue.js簡化座標計算跟更新資料,快速打造漂浮效果的輪播功能吧!

我們將以Code Pen做為本次實作的平台,這是一個可以在創作的同時即時看到程式碼運作狀況的線上程式碼編輯器,只要簡單註冊就可以使用囉!

如果想搭配直播影片一起實作,請往這邊走 👉🏻 https://www.youtube.com/watch?v=GFMnJKy_910

首先在Code Pen上開一個新的pen,將HTML的預處理器設定成Pug、CSS的預處理器設定成Sass、JS的CDN掛入Vue。

輪播中不可缺少的靈魂角色──圖片,這邊介紹一個可供個人也可商用的圖片網站Lorem Picsum(註一),他的特點是圖片是一個個的URL,使用時可直接嵌入網站。除了隨機圖片以外也可以透過/id/{image}指定特定圖片,尺寸大小的部分只要在URL後面加上寬和高就可以囉!另外也有像灰階、模糊等等的參數,可說是練習切版時的必備良藥。

*註一:直播影片中所提的Unsplash it目前已更新成Lorem Picsum,另有一個圖片平台Unsplash提供類似服務,但圖片取得方式為下載。

準備動態網頁的圖片與文案

在JS準備一個陣列works把輪播的材料塞進去,每個輪播分別有標題、敘述和圖片這些素材,在陣列中利用{}區隔。大家可以自由選擇喜歡的圖片跟文案,記得文案跟URL要用雙引號包起來。

套入輪播的材料:標題、敘述和圖片這些素材
套入輪播的材料:標題、敘述和圖片這些素材

接下來用Vue把資料套進來,首先在HTML建立#app,在JS建立一個新的Vue物件,指定Vue的作用範圍為#app並指定data的陣列來源為之前在外定義的works(為了讓Vue能夠使用所以在內部再定義一次)。輪播的原理很像幻燈片,透過指定每一組物件編號、計算他的寬度來達成輪播的效果。既然需要輪流,我們就必須設定起始點為陣列中的第0張now_index:0然後設定每張幻燈片的寬span為930px。

漂浮感輪播教學-設定Vue
設定Vue指定每一組物件編號、計算寬度

動態網頁內幻燈片的輪播是怎麼運作的呢?

把資料都套進來之後接下來要做重複與更新,這邊用以下三個畫面階層來解釋輪播的運作:

  1. Postarea:把元素固定在畫面的中央,有點像是逐格動畫中固定的畫框。
  2. Posts:多個靜態畫面組成的長條畫面。
  3. Postbox:長條畫面中的每一張靜態畫面。

在HTML裡建立上述三個階層的畫面,其中postbox中又包含了cover(圖片)、h1(標題)、h5(敘述)這三個元素。我們直接先用任一張圖片和文案做代表,使用語法v-for迭代陣列中的元素渲染六次模擬效果,之後再把文字和圖片替代進去成變數即可。

使用語法v-for迭代陣列中的元素渲染六次模擬效果,之後再把文字和圖片替代進去成變數即可
#app
  .postarea
    .posts
      .postbox(v-for="index in 6")
        .cover(:style="background-image:url('https://picsum.photos/id/1084/800/600')")
          h1 海象樂園
          h5 趴在冰層上的懶惰動物

有了基礎骨架後來準備拉皮,準備兩組Sass的mixin如下:

  1. flex_center:將物件保持在畫面的垂直水平正中央。
  2. size:快速設定物件的寬和高,考慮到時常會製作正方形的物件,設定成如果沒有額外填寫$h時,自動帶入$w的值。
@mixin flex_center
  display: flex
  justify-content: center
  align-items: center
  
@mixin size($w,$h: $w)
  width: $w
  height: $h

為了不讓畫面隨著播放整個一起移動(scroll),把畫面撐到跟視窗一樣100%大,然後設定overflow:hidden,再把背景設定成深色#1c1c1c。

小心網頁中繼承設定

接下來要設定app、postarea、posts、postbox這四者之間的位置和尺寸,要注意的是CSS觀念中子層如何繼承父層的設定,這樣才會知道在設定這麼多height:100%時到底是吃到誰的高度。先把最外層的#app size撐到100%,然後把現在為div形態的.postbox用語法display:inline-block變成水平(橫向),因為子層寬度大於父層時會自動往下折,所以.postarea需要用語法white-space:nowrap處理,其他寬高設定可以參考以下。

製作幻燈片

先前有說過輪播是透過幻燈片的推移來完成,剛開始我們在Vue有設定過每一張的幻燈片的寬度為930px span:930cover的尺寸為330px,做個小小的數學運算後,可知幻燈片之間的距離應為600px,左右各300px,用語法background-size:cover讓海象完整呈現。

把標題和敘述包在infos中,我們可以觀察到其實文案和圖片只有小部分的重疊,比較偷呷步的做法是把infos包在cover內,用translateX製造偏移。在瀏覽器中h1、h5有預設的margin記得取消,其他參數設定附上詳細的sass給大家參考,這樣單張的幻燈片就做好囉!

sass
.cover
    +size(330px, 100%)
    margin-left: 300px //930px-330px
    margin-right: 300px
    background-size: cover
    +flex_center
    
  .infos
    color: white
    transform: translateX(-200px) //運用X軸偏移製造部份重疊
    text-shadow: 0px 0px 30px rgba(0,0,0,0.3) //避免文案與圖片重疊辨識不清
    *
      margin: 0 //取消預設的h1、h5 margin
    
    h1
      margin-bottom: 10px
      font-size: 50px
      font-weight: 400
      
    h5
      background-color: #fff
      color: #000
      padding: 4px 12px
      font-size: 20px
      font-weight: 300
      box-shadow: 0px 0px 30px rgba(0,0,0,0.3)

準備好幻燈片後,接下來要計算如何播放,在Vue裡面定義一個新的計算屬性computed,內部再包一個會回傳一組CSS動態地套進去的functioncomputed_left。那要如何計算呢?既然.postarea是一個固定的畫框,我們想像.posts這個長條每偏移一次now_index都向左移一個span的寬度,在HTML加上:style ="computed_left",在CSS裡面的.posts加上position: relative才能吃到位置的資訊哦!最剛開始的時候還不用偏移,所以now_index值從0開始。用console.log可以看到偏移的計算結果,記得再return結果。

computed:{
    computed_left(){
      var result={
        "left": (-this.now_index * this.span) + "px"
      };
      console.log(result);
      return result;
    }
  }

動起來了是不是很感動!但你有沒有發現輪播跟已發車的火車一樣一去不回頭,通常我們希望輪播是會循環的,當在最後一張按下下一張的按鈕時,就會自動跳回第一張從頭開始。讓我們稍微思考一下變換的pattern,可以運用餘數和總長度的關係來呈現。

methods: {
    delta(d){
      // 0 1 2 3 4 
      // (-1 + 5) = 4
      // (5 % 5)=0
      // ((id +5) % 5) = 0
      // -1 => ((-1+5) % 5) = 4
      // 1 => ((1+5) % 5) = 1
      // 5 => ((5+5) % 5) = 0
      this.now_index =
        (this.now_index + d +this.works.length) % this.works.length
    }
  }

會循環的輪播才叫輪播

完成了左右切換循環連播的功能,現在來把先前的資料換成變數,原先寫成固定CSS style的cover改寫成Vue的functionbg_css,這樣資料就抽換完成了。

JavaScript

bg_css(url){
      return {
        "background-image": "url("+url+")"
      };
    }

HTML

#app
  .postarea
    .posts(:style="computed_left")
      .postbox(v-for="w in works")
        .cover(:style="bg_css(w.cover)")
          .infos
            h1 {{w.title}}
            h5 {{w.description}}

再加上互動按鈕

有了輪播的圖片,少不了左右切換的按鈕和使用者互動,把font-awesome(一個提供很多icon的免費平台)掛入CSS的CDN。

在font-awesome上選好左右icon後加在HTML內,在CSS設定按鈕的參數。

這邊大家可以根據自己的美感調整,通常在執行動作時都會加上transition讓互動不會那麼生硬,這時可以新增transition的mixin來統一整個網站的互動參數。

@mixin trans($t:0.5s, $td:0s)
  transition: $t $td
//t為變化持續的時間,td為延遲變化的時間

在HTML裡再用click把剛剛寫的function delta綁定在按鈕上,往左往右分別設定成1和-1,可用按鈕操作的輪播就大功告成啦!

最後起鍋前再加點鹽 最後再加一些微互動提升質感,通常互動可以以滑鼠事件為主,當使用者的滑鼠進入到特定區塊時,透過放大或偏移來增加點擊的機率。這邊提供老闆的做法讓大家參考,也可以想想還有什麼其他有趣的互動唷!

SASS

.cover
    +size(330px, 100%)
    margin-left: 300px //930px-330px
    margin-right: 300px
    background-size: cover
    background-position: center center
    +flex_center
    +trans
    cursor: pointer
    
    &:hover
      +size(340px,110%)
      .infos
        transform: translateX(-220px) translateY(-10px)

另外我們也可以透過Vue,在物件符合特定條件下時,才加入指定的動畫。這邊呈現的效果是當幻燈片切入的瞬間,從淺到深的透明度轉換效果。

HTML

當現在呈現的物件的id=now_index時,就會帶入cur_item的效果

.postbox(v-for="(w,id) in works", :class="{cur_item: id==now_index}")

CSS

針對cover設定一組名為fadeIn的keyframe,切換的瞬間會從透明度0變成透明度1,x軸也會稍微偏移,整組動畫的時間為1秒,動態為ease。infos也加上一些x軸偏移的效果增加速度感。

@keyframes fadeIn
  0%
    opacity: 0
    transform: translateX(30px) 
    filter: saturate(0%)
  100% 
    opacity: 1
    transform: translateX(0px) 
    filter: saturate(100%) 
 
@keyframes sliceIn
  0%
    transform: translateX(-50px) 
  100%
    transform: translateX(0px) 

.cur_item
  .cover
    animation: fadeIn 1s ease both
  .infos
    h5
      animation: sliceIn 1s 0.1s ease
```

以上就是這次如何用Vue.js快速做出輪播效果,從最一開始的資料處理、計算輪播距離和計算delta變化量轉換成座標,最後再加上一些玩轉的細節動畫。

希望大家還喜歡這次的小範例,如果對類似的網頁效果有興趣,歡迎來看看我們在Hahow所開設的動畫互動網頁程式入門(HTML/CSS/JS)以及動畫互動網頁特效入門(JS/CANVAS)有更多好玩的網頁動畫教學,那我們下次再見囉。

Codepen實作範例:https://codepen.io/frank890417/pen/bWrKOZ

此篇直播筆記由幫手 Jeudi Kuo 協助整理

墨雨設計banner

訂閱 Creative Coding Taiwan 電子報:

這篇文章 讓我們來快速寫個漂浮感的輪播吧!動態網頁程式教學(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
基礎JS物件導向教學(直播筆記) https://creativecoding.in/2021/03/31/js%e7%89%a9%e4%bb%b6%e5%b0%8e%e5%90%91%e9%9a%a8%e6%84%8f%e5%af%ab%e7%a8%8b%e5%bc%8f%e7%9b%b4%e6%92%ad%e7%ad%86%e8%a8%98/ Wed, 31 Mar 2021 10:35:00 +0000 https://creativecoding.in/?p=526 這篇文章老闆想跟大家分享物件導向,以及如何使用 js 結合物件導向概念包裝東西,創造並管理物件。 本文翻自 0212 隨意寫程式直播 / 物件導向,若是對文章內容有疑問,或是想要跟著影片一起做,都可以…

這篇文章 基礎JS物件導向教學(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
這篇文章老闆想跟大家分享物件導向,以及如何使用 js 結合物件導向概念包裝東西,創造並管理物件。

本文翻自 0212 隨意寫程式直播 / 物件導向,若是對文章內容有疑問,或是想要跟著影片一起做,都可以點入觀看影片詳細內容。

工商時間:老闆亂入在這打個小廣告 – 動畫互動網頁特效入門(JS/CANVAS)。網頁技術博大精深,學也學不完,老闆很開心不少同學在上過老師的課程後,成功加入 coder 的一員。 其他平台在對於 js 的課程教學內容上,已經很完善。但這門課不一樣的地方是,老闆會在課程中穿插不同的範例(例如摩斯密碼…等),有興趣的也可以支持一下。

目標

  1. 物件導向是什麼
  2. 使用 js 結合物件導向概念包裝東西

為什麼需要物件導向?

這邊我們舉個飛機射擊打怪獸遊戲作為例子。在這個遊戲中,操作者按下射擊會射出子彈,按鍵移動飛機…等功能,在監聽到前述事件後,需要不斷地增加子彈,或是針對飛機進行畫面位置改變,若只是規則簡易的小遊戲,這些都還能硬寫完成。但是當遊戲複雜度增加、要控制的物件增加時,就會讓製作過程非常痛苦。

再舉一個例子,假設今天 人物A 要將 蘋果 交給 人物B 的過程,若是不看畫面,在程式碼內應該要看到三個物件(人物A 、人物B、蘋果),程式碼就會像下面這樣:

A.給(B, 蘋果)

我們把非常繁雜的物件、屬性包成一個抽象的概念,讓他們之間去做互動,管理物件的方式,讓大家在寫程式的時候,不會被底層的內容給干擾。需要使用物件導向的原因,就是因為我們需要一個抽象的介面來處理這些事情。希望能夠透過物件導向,將程式碼簡潔,也不會讓遊戲分數或是各種邏輯條件的程式碼四散各處。

接下來我們來一起寫一些程式,加深對物件導向的理解。

邁向物件導向

要什麼就宣告什麼

我們先來建立一個會打招呼的人,他的名字叫Frank:

var person_name = 'Frank'
var personSpeak = function (name) {
  console.log('Hello, ' + name)
}
personSpeak(person_name) // Hello, Frank

這樣寫會發現一個狀況,人名和他的動作(Speak)是分開的,這樣對於管理並不友善,所以接下來我們嘗試將人名與說話的方法統一管理。

物件屬性統一管理

接著我們嘗試結合物件,將跟人有相關的屬性(名字、性別、年紀…等)包成物件。這種方式讓函式可以吃到人的所有屬性,也可以針對此來做更多的變化,我們試著把程式碼改動成:

var person = {
  name: 'Frank',
  gender: 'Male'
}
function personSpeak (person) {
  console.log('Hello, ' + person.name, person.gender)
}
personSpeak(person) // Hello, Frank, Male

將 function 也放進物件中

但是當程式越來越大,人有太多的屬性,每次呼叫 function 都要傳人這個參數進去,會覺得有點麻煩。這時候,我們開始思考,是不是要連名字也一起放進來,就不用在呼叫 function 的時候,還要傳人的屬性進去,所以我們把程式碼改動成:

var person = {
  name: 'Frank',
  gender: 'Male',
  speak: function () {
    console.log('Hi, I\'m ' + this.name + '!')
  }
}
person.speak() // Hi, I'm Frank!

這個概念可以延伸到我們在寫遊戲,要讓遊戲物件棋子往前,不需要呼叫往前並傳入棋子這個參數,而是針對這個棋子呼叫往前的 function。

類別定義、函數產生器

當只要產生一個人的時候,我們宣告物件很容易,但是當今天要產生五個、十個、甚至一百個的時候,我們不可能打一百個 var person98… var person99… var person100 吧?所以接下來將這種宣告方式改寫成物件產生器。我們將這種物件產生器稱為「類別定義(模板)」。現在不管要幾個人,我們就可以使用這種方式產出無限多人了!

🔔小叮嚀:類別為抽象概念,程式語言的習慣上也會將類別的首字大寫

var Person = function (name, gender) {
  this.name = name
  this.gender = gender
  this.speak = function () {
    console.log('Hi, I\'m ' + this.name + '!')
  }
}
var person1 = new Person ('Frank', 'Male')
var person2 = new Person ('Mick', 'Female')
person1.speak() // Hi, I'm Frank!
person2.speak() // Hi, I'm Mick!

抽出共同方法

現在我們使用 chrome 的開發者工具,檢查 person1, person2 的結構會發現。產生器所產出的每個物件,包進去的函式都是一個新的 function ,並不是共用的。

person1.speak = function () {
  console.log('Hello, I\'m ' + this.name + '!')
}
person1.speak() // Hello, I'm Frank!
person2.speak() // Hi, I'm Mick!

這時狀況來了,假設今天我們要改變某一個人說話這個函式的內容,就必須一個一個去修改。這樣的操作顯得有點不人性化。我們希望能夠在修改函式的時候,只需修改一次。所以我們可以試著將共同的函式抽出,只要是抽象定義共用的,我們就將它拉出來,而不是再複製一份。

我們針對人這個類別,將共同的函式抽出,改使用 prototype(原型,不會變動的,有點像是人的根源的概念)。

var Person = function (name, gender) {
  this.name = name
  this.gender = gender
}
// 將共同方法抽出
Person.prototype.speak = function () {
  console.log('Hi, I\'m ' + this.name + '!')
}
var person1 = new Person ('Frank', 'Male')
var person2 = new Person ('Mick', 'Female')

person1.speak() // Hi, I'm Frank!
person2.speak() // Hi, I'm Mick!

// 修改共同方法
Person.prototype.speak = function () {
  console.log('Hello, I\'m ' + this.name + '!')
}

//說話從Hi變成了Hello
person1.speak() // Hello, I'm Frank!
person2.speak() // Hello, I'm Mick!

這時候再使用開發者工具檢查 Person,會發現它的結構變成如下,只有兩個屬性,沒有打招呼的函式,理論上是無法說話,那為什麼 person1 還能說話呢? 發現他們參考到相同的源頭 _proto_

這個函數共用的概念,可以拿來做許多的應用,例如彈珠檯遊戲,每個球都有自己的位置和移動,我們就可以用這個概念去產出許多顆彈珠。

繼承 – 抽取出更抽象的類別

現在的物件產生器,不僅可以幫我們快速產出一個又一個的人,並且擁有一樣的函式。以為這樣就結束了嗎?太小看物件導向了。像是球球對打的遊戲中,球和兩個板子雖然完全不相干,但是我們可以發現,他們除了外型不同之外,其實有許多共同屬性與方法(例如x, y的座標、移動、碰撞),所以我們是不是可以依照需求,抽取出更抽象的類別。

js 內的繼承非常的麻煩,若是想要了解更詳細的內容,也可以參考動畫互動網頁特效入門(JS/CANVAS)課程。在課程內的範例,是以狗去繼承生物體的屬性。狗和人都是生物,都會有名字、生命長度…等基本屬性,但是狗自己又有品種、喜歡吃的東西等屬性和方法,這時候我們就可以執行繼承,讓狗除了自己特有的屬性外,還能夠繼承生物體本身的基本屬性。

剛剛我們已經製作好人的產生器,並且加了賦予他們說話能力的函式,現在我們多了一個新的類別 – 工作者WorkPerson,會有自己額外的屬性(自己的工作)。我們並不會想再把人的屬性寫一遍,額外多一筆工作的值,這種寫法非常地冗,該如何讓 WorkPerson 使用人的屬性呢?首先讓我們整理出這次的三個新名詞:

  1. 繼承:類別繼承到物件,產生的人都要能有這些方法, WorkPerson 要能回去參考 Person 說話的方法
  2. prototype:原型、不會動的
  3. _proto_:共同源頭

操作的步驟如下:

繼承者有原始的屬性可以使用 – call,WorkPerson 初始化時,要使用 Person 的屬性執行
我們把程式碼改寫成如下:

var Person = function (name, gender) {
  this.name = name
  this.gender = gender
}
Person.prototype.speak = function () {
  console.log('Hi, I\'m ' + this.name + '!')
}

// 錯誤寫法
var WorkPersonFalse = function (name, gender, work) {
  this.name = name
  this.gender = gender
  this.work = work
}

// 正確寫法 - 步驟1
var WorkPerson = function (name, gender, work) {
  Person.call(this, name, gender)
}
var person = new WorkPerson('Amy', 'Female', 'designer')

這時候我們可以從開發者工具看到 person 已經有 Person 宣告的屬性了。

但此時,我們發現 person 還不能參考到 Person 的方法 speak。所以我們接下來再做些處理

// 步驟 2 
var WorkPerson = function (name, gender, work) {
  Person.call(this, name, gender)
  this.work = work
}

// 錯誤寫法
WorkPerson.prototype = Person.prototype
// 正確寫法
WorkPerson.prototype = Object.create(Person.prototype)

WorkPerson.prototype.speakWork = function () {
  console.log('I\'m ' + this.work + '.')
}

var person = new WorkPerson('Amy', 'Female', 'designer')

那為什麼我們不能直接寫成 WorkPerson.prototype = Person.prototype 呢?

因為如果我們今天針對 WorkPerson.prototype.speak 去修改,也會連帶修改到 Person 中的 speak 方法,這並不是我們希望達到的效果,我們希望的只是 WorkPerson 參考 Person 的方法。

當用錯誤方式去宣告,造成 WorkPerson 有一個說自己工作的函式時, Person 也會有這個函式。

複寫

老闆在這邊也給大家一個小提示,今天當 WorkPerson 自己也有 speak 的函式時,它就會優先使用自己有的。當它沒有這個函式時,才會向它繼承的對象去找有沒有這個方法。

Person.prototype.speak = function () {
  console.log('I\'m ' + this.work + '.')
}

WorkPerson.prototype.speak = function () {
  console.log('Aloha.')
}

以上為物件導向的過程,大家可以一起跟著寫寫看,透過實際操作了解之中的差別。也因為現在工具越來越方便,比較少會去直接接觸到這些內容,若是大家想要對程式有更深的了解,我們建議大家還是可以去了解一下底層的概念。

動畫互動網頁特效入門(JS/CANVAS)課程裡面,分成三個章節在講物件導向:概念篇、繼承篇及實作篇,有更詳細的介紹,再完成一個小遊戲。

老闆來閒聊

另外老闆也跳出來和大家分享,在老闆進行網頁設計的過程,常用的一些軟體和工具。

網頁設計

老闆在製作遊戲前,都會先畫個草圖,來敘述遊戲配置和過關條件。製作網頁時,老闆會使用模板工具( Zeplin, Sketch, Illustrator.. )來做內部與外部的設計稿討論、tag 討論。 動畫互動網頁特效入門(JS/CANVAS)會和大家分享一些動態網頁的形式與實際應用,以及怎麼使用 Sketch 以及 Zeplin 來執行專案,有興趣想深入了解老闆怎麼利用這些工具進行設計,一樣可以支持一下老闆的課程。

後端 – firebase

老闆在這邊跟大家誠摯推薦 – firebase 。 firebase 可以做為後端資料庫使用外,也支援檔案上傳,取得 url 就可以直接將圖片引用到你的網站中。作品集若是使用 firebase 串的,若是在後台更改後,可以馬上在前端看到畫面上的變動。也可以利用 firebase 來製作權限功能,提供不同權限的人有不同功能。

案子分享 – 雜學校

這個案子屬於單頁式的 spa ,透過動態轉換,不會有換頁效果。也因為是單頁應用程式,所有狀態是同步的。現在的網站會把整個網站想像成一個應用程式,不像以往是一頁一頁區分的,好處是通用狀態不用存在後端 php session內,也不會有換頁必須要重整頁面的狀況。

這次的分享就到這邊,如果有甚麼問題,都歡迎私訊老闆 來點寇汀吧粉專,或是到我們的Instagram上看看最近都在玩些甚麼吧!

此篇直播筆記由幫手 H 協助整理

墨雨設計banner

訂閱 Creative Coding Taiwan 電子報:

這篇文章 基礎JS物件導向教學(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>