CSS 彙整 | Creative Coding TW - 互動程式創作台灣站 https://creativecoding.in/tag/css/ 蒐集互動設計案例、教學與業界資源,幫助你一起進入互動程式創作的產業 Tue, 17 Aug 2021 13:47:14 +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 CSS 彙整 | Creative Coding TW - 互動程式創作台灣站 https://creativecoding.in/tag/css/ 32 32 Vue.js入門:英文2000字互動遊戲網頁 https://creativecoding.in/2021/08/26/vue-js-%e8%8b%b1%e6%96%872000%e5%ad%97%e4%ba%92%e5%8b%95%e9%81%8a%e6%88%b2%e7%b6%b2%e9%a0%81/ Thu, 26 Aug 2021 01:38:00 +0000 https://creativecoding.in/?p=1397 你多久沒鍛鍊英文了呢?這次直播使用HTML(Pug)CSS(Sass)以及Javascript(Vue.js)來完成一個練習2000字英文單字的小互動遊戲網頁,難度適中,尤其適合剛開始學習Vue.js的新手,無論是跟著步驟逐步操作,或是聽老闆的影片都能快速完成。

這篇文章 Vue.js入門:英文2000字互動遊戲網頁 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
英文2000字選擇題互動網頁完成圖
英文2000字選擇題互動網頁完成圖

這次直播的主題來自於老闆在2018年印象清華-物聯網科技藝術展中創作的展品〈英文8-2〉,其概念為十根代表清大不同學院的光柱,使用者只要掃描光柱上的QR Code就會跳出互動式的英文單字題目,只要答對題數越多、分數就越多,累積的分數便會即時地投射到光柱上,形成高高低低、動態交錯的有趣光景。

在這次直播中會來聊些這個專案內使用Vue的相關經驗,聊聊製作互動裝置藝術實作時整合的各種辛酸血淚史,以及如何快速地解析別人資料,利用Vue框架製作成幫你找回國中逝去英文能力的遊戲。

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

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

前期準備

在開始之前我們根據概念來進行規劃,想像一個英文單字的互動答題App需要哪些東西:

  1. 整理網路上現成的單字表,把資料變成符合我們條件的JSON格式物件,單字表必須同時具備英文、中文與詞性(今天借用的是109英文銜接教材2000單字
  2. 產生隨機的英文題目,並利用整理後的物件選出正確答案和其他類似的詞當作選項,並判斷答題者的正確與否
  3. 如果答題者正確,跟後端同步狀態累加分數

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

程式環境設定
程式環境設定

接著把單字表上的文字複製下來貼到Javascript中,var一個a,並且用ES6的頓號 ` 把文字包起來。

//Javascript
var a = `
A
able adj. 有能力的
about prep. 有關
above adv. 在上方
`

註:考慮篇幅關係這邊只貼上部分A字首的單字,實際資料請參考單字表。

我們快速分析一下他的架構組成,參考字首A的部分得知單字表主要可以分為:字首的段落開頭、英文、詞性、中文,也就是──只要是沒有英文單字的那一行就不會有「.」,如果說我們今天要把單字整理成一個一個的物件時,可以把每一行先分割出來、把含有「.」的留下,再分別拆解成英文、詞性、中文,這就是我們所需的資料。

1. 拆解單字表

利用語法split以空行來做分隔,再用語法filter把含有.的行過濾保留下來,利用list.lengthlist[n]在console查看過濾後的listlist2數量上的差異,代表我們的資料越來越乾淨了。利用語法map把原先陣列的一行一行轉化成一個一個,再存成另一個陣列,轉換的條件為用空格分割,console會發現list3裡面裝著一坨拉庫的[object Array](3)。再把list3拆分成wordcatatrans,分別對應英文、詞性、中文的物件。

//Javascript
var list = a.split("\n") //分割空行
var list2 = list.filter(item=>item.indexOf(".")!=-1) //過濾沒有.
var list3 = list2.map(item=>item.split(" ")) //單行轉單個
var list4 = list3.map(item=>({
  english: item[0],
  cata: item[1],
  trans: item[2]
})) //拆分成英文、詞性、中文

資料搬運小幫手Vue

Vue的特色在於資料雙向綁定,相較於jQuery需要選取物件、重新定義、再塞回去以直接操作 DOM 物件為主的方式,利用Vue的同步更新渲染資料可以幫助我們節省不少時間。Vue的寫法為在JS透過 new Vue建立作用範圍和待即時同步的資料,同時在HTML以{{}}包裹被更新的變數。以下為官方網站所舉的範例,Vue會將大括號{{}}的內容對應到message狀態,並且將之即時渲染至畫面上,也就是所指的「宣告式渲染」。

//HTML
<div id="app">
  {{ message }}
</div>
//Javascript
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

延伸閱讀: 「Vue.js 學習筆記 Day1」- 建立 Vue 應用程式 重新認識 Vue.js | Kuro Hsu

2. 實際運用Vue-單字小卡

運用Vue和剛剛整理好的單字表資料來試做一些英文小卡吧!取出list4中倒數20個單字用v-for迭代陣列中的物件,指定其資料種類並渲染在li的span,再給一些CSS的參數後就可以看到一張張排列整齊的你國中的惡夢單字小卡。透過Vue的幫忙,我們不用自己產生元件跟呈現,只需要確定資料是否正確即可。

//HTML
#app 
    h2 我的名字是{{name}}
    ul
      li(v-for="word in words")
        span {{word.english}} {{word.cata}} {{word.trans}} //指定word中的種類
//CSS
html,body
  background-color: #222

ul
  li
    background-color: #fff
    padding: 20px
    display: inline-block
    margin: 20px
    width: 200px
//Javascript
var vm = new Vue({
  el: '#app',
  data: {
    name: "Frank",
    catas: ["a","b","c","d"],
    words: list4.slice(-20) //負號代表從後面數來的20個單字
  }
})
英文2000字選擇題互動網頁:步驟二,製作出單字卡
英文2000字選擇題互動網頁:步驟二,製作出單字卡

3. 製作答題選項

製作完單字小卡有沒有覺得長得很像我們的答題選項呢?在Vue中我們定義methods為操作不同 DOM 元素的方法,這邊需要綁定幾個動作:

  1. click DOM元素根據滑鼠點擊的動作,在console回傳所點擊的單字。
  2. getOptions挑選同詞性、同字首的隨機四個單字。用filter過濾word.cata == question.cata也就是詞性需與答案相同,過濾第二次word.english[0] == question.english[0]英文單字的字首(第0個字)需相同,過濾第三次word.english !== question.english確認答案不會等於題目。
  3. 使用sort把陣列的順序打亂:.sort((a,b)⇒a-b)是將大的往後排,但sort((a,b)=>Math.random())則是隨機取值,再扣掉比較函數0.5後成為真正隨機排序的陣列,加上.slice(0,4)限縮在一次只取四個單字。
  4. .sort前面加入第二個.slice()把原本的元素複製一份成新的陣列,避免影響到既有陣列的順序,雖然針對新的陣列動屬性依然會影響原先的物件,但兩個陣列的順序是分開的。
  5. 亂數打亂正確答案的位置,目前都是把正確答案推到最前面,取得result後用concat連接question這個陣列,再打亂一次排序,成為result2
//HTML
#app 
  h2 我的名字是{{name}}
  ul
    li(v-for="word in words", v-on:click="click(word.english)") //讓console顯示出滑鼠點擊到哪個英文單字
      span {{word.english}} {{word.cata}} {{word.trans}}
//Javascript
var vm = new Vue({
  el: '#app',
  data: {
    name: "Frank",
    catas: ["a","b","c","d"],
    words: list4
  },
  methods:{
    click(word){
      console.log("click",word)
    },
    getOptions(question){
      let result = this.words.filter(
      word => word.cata == question.cata).filter(
      word => word.english[0] == question.english[0]).filter(
      word => word.english !== question.english
      ).slice().sort((a,b)=>Math.random()-0.5).slice(0,4)
      let result2 = result.concat([question]).slice().sort((a,b)=>Math.random()-0.5)
      return result2
    }
  }
})

註:concat只能做陣列與陣列的連接。

4. 建立一個出題目按鈕,產生新題目

methods新增pick(),在data中定義還沒開始之前question: null,用this存取本身的資料屬性,隨機選取陣列裡的其中一個字,記得因為index須為整數所以加上parseInt

//HTML
#app 
  button(@click="pick") 出題囉
  h2(v-if="question") {{ question.english }}
//CSS
html,body
  background-color: #222
  color: #fff
//Javascript
var vm = new Vue({
  el: '#app',
  data: {
    ...
    question: null
  },
  methods:{
    click(word){
      console.log("click",word)
    },
    pick(){
      this.question = this.words[parseInt(Math.random()*this.words.length)]
    },
    getOptions(question){
      ...
    }
  }
})
英文2000字選擇題互動網頁:步驟四,出題按鈕
英文2000字選擇題互動網頁:步驟四,出題按鈕

5. 建立選項

把單字的中文抓出來印成題目,同時也把選項抓出來存取,一開始會是空的陣列所以在data定義options: [],再在pick()中多加一行程式碼把產生的新題目裝回去。

//HTML
#app 
  button(@click="pick") 出題囉
  h2(v-if="question") {{ question.trans }}
    ul
      li(v-for="option in options") {{option.english}}
//CSS中要先把li的樣式暫時註解掉
//Javascript
var vm = new Vue({
  el: '#app',
  data: {
    ...
    question: null,
    options: []
  },
  methods:{
    click(word){
      ...
    },
    pick(){
      this.question = this.words[parseInt(Math.random()*this.words.length)]
      this.options=this.getOptions(this.question)
    },
    ...
  }
})
英文2000字選擇題互動網頁:步驟五,建立選項
英文2000字選擇題互動網頁:步驟五,建立選項

6. 判斷答案正確與否

比較簡單的做法是在產生資料時同時附加他是否正確的資訊在其中,我們複製一份新的question避免影響原本的,在let result2前面加上let questionClone = JSON.parse(JSON.stringify(question)),再把帶有正確與否屬性的物件混到原有的選項中,把questionClone作為判斷的正確答案,而result2中原本的question也要記得替換成questionClone

methods新增check(option),點擊選項時如果正確,console印出correct、不正確則印出wrong,回答完後再重新出題this.pick()

//HTML
...
ul
  li(v-for="option in options",
     @click="check(option)") {{option.english}}
//Javascript
...
  methods:{
    click(word){
      console.log("click",word)
    },
    check(option){
      if (option.correct){
        console.log("correct")
      }else{
        console.log("wrong")
      }
      this.pick() //點選答案無論對錯都會換下一題
    },
    pick(){
      ...
    },
    getOptions(question){
      let result = this.words.filter(
      word => word.cata == question.cata).filter(
      word => word.english[0] == question.english[0]).filter(
      word => word.english !== question.english
      ).slice().sort((a,b)=>Math.random()-0.5).slice(0,4)
      let questionClone = JSON.parse(JSON.stringify(question))
      questionClone.correct=true
      let result2 = result.concat([questionClone]).slice().sort((a,b)=>Math.random()-0.5)
      return result2
    }
  }
})
英文2000字選擇題互動網頁:步驟六,console顯示出選擇了正確或錯誤答案
英文2000字選擇題互動網頁:步驟六,console顯示出選擇了正確或錯誤答案

7. 增加答題分數計算機制以及顯示正確或錯誤

要增加答題分數grade的計算機制,首先在data中定義grade: 0,在check(option)中如果答對了就加一分this.grade++,並在HTML中顯示。

//HTML
#app 
  h3 Score:{{grade}}
  ...
//Javascript
...
  data: {
    ...
    grade: 0
  },
  methods:{
    click(word){
      ...
    },
    check(option){
      if (option.correct){
        console.log("correct")
        this.grade++
      }else{
        console.log("wrong")
      }
      this.pick()
    },
    pick(){
      ...
    }
  }

目前答題正確與否只能靠分數是否有增加得知,要改成更直觀一點,點擊選項時如果正確,在題目右邊會印出correct並累加分數this.grade++、不正確則印出wrong,我們使用一個預設是空字串的變數status去儲存這個資訊,status設定過1秒後消失,回答完後再重新出題this.pick()

通常使用者在進到介面時題目已經出好了,答題後會自動更新,所以頁面剛載入時便自動執行一次pickmounted代表Vue已經準備好可以幫忙計算資料了,所以跟methodsdatael在同一層級。

//HTML
#app 
  .container
    .row
      .col-sm-12
        h3 Score:{{grade}}
        h2(v-if="question") Q: {{question.trans}}
          .status {{status}}
        ul 
          li(v-for="option in options", @click="check(option)") {{option.english}}
//Javascript
var vm = new Vue({
  el: '#app',
  data: {
    name: "Frank",
    catas: ["a","b","c","d"],
    words: list4,
    question: null,
    options: [],
    status: "",
    grade: 0
  },
  mounted(){
    this.pick()
  },
  methods:{
    click(word){
      console.log("click",word)
    },
    check(option){
      if (option.correct){
        this.status =("correct")
        this.grade++
      }else{
        this.status = ("wrong")
      }
      setTimeout(()=>{
        this.status=""
        this.pick()
      },1000)
      this.pick()
    },
    pick(){
      this.question = this.words[parseInt(Math.random()*this.words.length)]
      this.options = this.getOptions(this.question)
    },
    getOptions(question){
      let result = this.words.filter(
      word => word.cata == question.cata).filter(
      word => word.english[0] == question.english[0]).filter(word => word.english !== question.english).slice().sort((a,b)=>Math.random()-0.5).slice(0,4)
      let questionClone = JSON.parse(JSON.stringify(question))
      questionClone.correct = true
      let result2 = result.concat([questionClone]).sort((a,b)=>Math.random()-0.5)
      return result2
    }
  }
})
英文2000字選擇題互動網頁:步驟七,增加答題對錯的顏色回饋

8. 設計畫面及加入動畫

完成骨幹後我們來稍微美化一下吧!在codepen設定CSS的地方引入Bootstrap和Animate.css,把DOM元素放到container內,讓版面豐富一些可以加入hover的滑鼠互動效果和答對或答錯時相對應的變色效果。

變色效果我們可以利用Vue的特性來製作──透過判斷式給予class,也就是判斷當前的status為correct或wrong,如果是correct則顯示綠色、wrong則顯示橘色,必須注意的是由於其他四個錯誤答案的status同時都會是wrong,所以要多下一個判斷點currentOption記錄只有點選的這個選項是wrong時才顯示橘色。在data定義currentOption: {}methodscheck(option)的加入條件this.currentOption = option

英文2000字選擇題互動網頁:步驟八,增加答題對錯的顏色回饋
英文2000字選擇題互動網頁:步驟八,增加答題對錯的顏色回饋

淡入的效果我們則透過Animate.css和Vue的key來製作,每次物件重新產生時都帶有新的english值,所以我們給予的key值也會不一樣,這樣他的animated.fadeIn效果就會重新被載入。

//HTML
#app 
  .container
    .row
      .col-sm-12
        h3 Score:{{grade}}
        h2.animated.fadeIn(v-if="question",:key="question.english") Q: {{question.trans}}
          .status {{status}}
        ul.animated.fadeIn(:key="question.english")
          li(v-for="option in options", @click="check(option)",:class="{correct: status=='correct'&& option.correct, error:status=='wrong' && currentOption.english==option.english}") {{option.english}}
//CSS
ul
  list-style: none
  padding: 0
  li
    padding: 10px
    margin-top: 20px
    border: 1px solid white
    cursor: pointer
    font-size: 30px
    transition: .5s
    &:hover
      background-color: rgba(white,0.1)
    &.correct
      background-color: #38d138
    &.error
      background-color: #ff7332
      
.status 
  float: right
//Javascript
...
var vm = new Vue({
  el: '#app',
  data: {
    name: "Frank",
    catas: ["a","b","c","d"],
    words: list4,
    question: null,
    currentOption: {}, //給予{}以防報錯
    options: [],
    status: "",
    grade: 0
  },
  mounted(){
    this.pick()
  },
  methods:{
    click(word){
      console.log("click",word)
    },
    check(option){
      this.currentOption = option //加入條件
      if (option.correct){
        this.status =("correct")
        this.grade++
      }else{
        this.status = ("wrong")
      }
      setTimeout(()=>{
        this.status=""
        this.pick()
      },1000)
    },
    pick(){
      this.question = this.words[parseInt(Math.random()*this.words.length)]
      this.options = this.getOptions(this.question)
    },
    getOptions(question){
      let result = this.words.filter(
      word => word.cata == question.cata).filter(
      word => word.english[0] == question.english[0]).filter(word => word.english !== question.english).slice().sort((a,b)=>Math.random()-0.5).slice(0,4)
      let questionClone = JSON.parse(JSON.stringify(question))
      questionClone.correct = true
      let result2 = result.concat([questionClone]).sort((a,b)=>Math.random()-0.5)
      return result2
    }
  }
})

以上就是這次的英文2000字即時互動小遊戲網頁的製作介紹拉,這次講解了許多Vue的基礎概念與用法,如果是剛開始接觸Vue的朋友很適合拿來小練身手唷!我們下次再見啦~

成品請參考這邊 👉🏻 https://codepen.io/frank890417/pen/RygVde

英文2000字選擇題互動網頁成果

重點回顧:

  1. 利用filter()、sort()、slice()整理與排序資料
  2. 宣告式渲染的使用方式,資料與function的對應關係
  3. 動態判斷class製作css animation效果

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

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

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

墨雨設計banner

這篇文章 Vue.js入門:英文2000字互動遊戲網頁 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
利用Pug(HTML)、Sass(CSS)及Vue.js製作動態時間軸年表(直播筆記) https://creativecoding.in/2021/08/04/%e5%88%a9%e7%94%a8pug-sass-vue-js%e8%a3%bd%e4%bd%9c%e5%8b%95%e6%85%8b%e6%99%82%e9%96%93%e8%bb%b8%e5%b9%b4%e8%a1%a8/ Wed, 04 Aug 2021 03:36:00 +0000 https://creativecoding.in/?p=1320 不論個人或是品牌,都需要讓網站造訪者快速暸解你的歷史發展,時間軸年表是一個常見且好用的表示方法。利用Pug(HTML)、Sass(CSS)及Vue.js製作出一個屬於你的動態時間軸年表吧!

這篇文章 利用Pug(HTML)、Sass(CSS)及Vue.js製作動態時間軸年表(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>

凡走過必留下痕跡,時間軸年表常應用於紀錄故事和里程碑上,更是構築形象的第一步。不論是用於個人或是品牌,都能幫助網站造訪者能快速暸解關於你的歷史發展,一個常見且好用的表示方法。

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

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

在開始製作年表之前我們需要準備以下步驟:

  1. 資料──也就是呈現在年表上的內容,舉凡文案、年份、圖片等資料,一般是儲存在資料庫內
  2. 版面配置──物件位置關係規劃、色票和尺寸library建置
  3. 插入資料──讀取資料的trigger和loading動畫

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

《動態時間軸年表》開發環境設置
《動態時間軸年表》開發環境設置

資料準備

首先來準備資料,通常年表會包含這些資料,標題、內容、年份、日期,資料儲存的形式會是每一筆都有自己的年月份,再依照日期順序做排列。命名一個陣列為logs在裡面輸入yearcontent等內容,在content內使用另外一個陣列再做資料渲染彈性會比較大,也可以加入不同的tag區分同年份中的不同事件。

資料的樣式可參考以下範例:

//JS
var logs = [
  {
  year: 2014,
  content: [
    {
      tag: "開始接網頁專案",
    },
    {
      tag: "開始接網頁專案2"
    }
  ]
}]

在console裡面輸入logs,如果出現陣列代表資料準備完成。

《動態時間軸年表》陣列資料準備完成
《動態時間軸年表》陣列資料準備完成

接著我們把資料用ul列表的方式印出來,在JavaScript定義一個新的Vue並設定它的作用範圍、套用的資料為logs。在HTML的ul列表內,把logs印成一筆一筆的li,這時會看到我們原先設定的四筆資料,並且分為year、content等細部內容。

//JS
var vm = new Vue({
  el: "#app",
  data: {
  logs: logs
  }
});
//HTML
#app
  ul
    li(v-for="l in logs")
      h3.year {{l.year}}
      ul.content
        li(v-for="c in l.content") {{c.tag}}
《動態時間軸年表》資料顯示
《動態時間軸年表》資料顯示

由於某些網頁的HTML標籤會帶有原生的CSS,比如剛剛使用的ul自帶圓圈和margin的樣式,我們在codepen引入reset CSS做樣式重置。

延伸閱讀:[CSS] 跨瀏覽器的樣式重置 reset.css & normalize.css

版面配置

樣式重置完後我們就可以來做設計發想啦,時間軸年表的排列比較複雜,可以參考下面的簡易草稿來構思物件之間的位置關係。我們用 dialog_wrapperdialog 分別對應下圖的年份與事件,在HTML包覆相對應的資料。

《動態時間軸年表》設計發想
《動態時間軸年表》設計發想
//HTML
#app
  ul
    li.dialog_wrapper(v-for="l in logs")
      .dialog
        h3.year {{l.year}}
        ul.content
          li(v-for="c in l.content") {{c.tag}}

接下來建置色票和常用尺寸的library,本次使用的色票如下,常用到的寬高也建置 mixin 模組方便快速取用:

//CSS
$color_light_blue: #D4EBE8
$color_dark_blue: #4FBDBC
$color_white: white
$color_yellow: #F4DF38
$color_orange: #F4A373

@mixin size($w, $h: $w) //如果寬高數值一樣,取寬
  width: $w
  height: $h

這邊介紹大家一個好用的語法縮寫網站──Sass cheatsheet,熟練這些語法的話就可以增進切版的速度唷!

在版面設計的部分我們給背景壓上一層淡藍色,然後撐開寬高到100%。接著處理dialog的部分,可以切分為以下幾個元件:

//CSS
body
  background-color: $color_light_blue
  +size(100%)

.dialog
  // dialog本體的樣式設定
  background-color: #fff
  padding: 15px 20px
  cursor: pointer
  
  border-radius: 5px
  box-shadow: 15px 15px $color_dark_blue
  width: 250px
  position: relative
  transition: 0.5s // 漸變動畫較柔和
  
  // 裝飾性小方塊設定
  &:before
    content: ""
    display: block
    +size(20px)
    border-radius: 3px
    position: absolute
    right: -10px
    background-color: $color_white
    transform: rotate(45deg)
  //滑鼠移上去時,方塊往左上方、陰影往右下方移動
  &:hover
    transform: translate(-10px, -10px)
    box-shadow: 20px 20px $color_dark_blue
    
  
  // 標題文字設定
  .year
    font-size: 36px
    font-weight: 700
    margin-bottom: 10px
    letter-spacing: 2px

如果對於偽元素的運用不是那麽地熟悉,可以參考這篇文章──CSS 偽元素 ( before 與 after )

接著長出 timeline 讓他在畫面中上下左右置中。根據我們上方的草稿, dialog 的位置其實是由 dialog_wrapper 的相對關係所決定的,所以給予一些高度後在 dialog_wrapper 上增加 position: relative ,在 dialog 上改為 position: absolute

//HTML
#app
  ul.timeline //加個.timeline
    li.dialog_wrapper(v-for="l in logs")
      .dialog
        h3.year {{l.year}}
        ul.content
          li(v-for="c in l.content") {{c.tag}}
//CSS
#app
  display: flex
  align-items: center
  justify-content: center

.timeline
  height: 100vh
  width: 6px
  background-color: rgba($color_white, 0.4)
  padding-top: 50px

.dialog_wrapper
  height: 160px
  position: relative

.dialog
  ...
  position: absolute
《動態時間軸年表》製作時間軸以及事件外框樣式
《動態時間軸年表》製作時間軸以及事件外框樣式

那要如何讓 dialog 左右交錯排列呢?我們可以在 dialog_wrapper 裡面把它分為偶數和單數,用語法 :nth-child 選擇第 2n2n+1 個,可以暫時設定不同的顏色有助於判別。接著調整 dialogtimeline 的距離,記得 left 的值會優先於 right ,所以在偶數排設定 left: initial ,再透過偽元素 &:before 調整偏右對話框的小尾巴。

//CSS
.dialog_wrapper
  ...
    &:nth-child(2n+1)
    background-color: blue
    .dialog
      left: 40px
      &:before
        left: -10px
  &:nth-child(2n)
    background-color: red
    .dialog
      right: 40px
      left: initial

.dialog
  ...
  right: 0
《動態時間軸年表》左右交錯排列
《動態時間軸年表》左右交錯排列

接著利用 dialog_wrapper 的偽元素做出時間軸上的圓圈點,我們的時間軸年表樣式大致上完成囉。

//CSS
.dialog_wrapper
  height: 160px
  position: relative
  &:before
    content: ""
    display: block
    +size(20px)
    border: solid 5px white
    border-radius: 50%
    left: 50%
    transform: translateX(-40%)
    left: 0
    top: 0px
  ...

插入資料

新增用來插入資料的 button ,修改初始時 logs 為空值,定義他的 methodsinitial 時動態等於一開始所定義的 logs

//HTML
#app
  button.initial(@click="initial") 插入資料
  ul.timeline
  ...
//CSS
button.initial
  position: fixed
  right: 50px
  bottom: 50px
  background-color: $color_dark_blue
  color: white
  border: none
  border-radius: 5px
  padding: 5px 10px
  font-size: 16px
  cursor: pointer
//JS
//將JavaScript兩段程式碼濃縮成為一個
var vm = new Vue({
  el: "#app",
  data: {
    logs: []
  },
  methods: {
    initial(){
      this.logs=[];
      this.logs=[
        {
          year: 2014,
          content: [
            {tag: "開始接網頁專案",
            }
          ]
        },
        {
          year: 2015,
          content: [
            {tag: "成立墨雨設計工作室",
            }
          ]
        },
        {
          year: 2016,
          content: [
            {tag: "開設動態互動網頁程式入門",
            }
          ]
        },
        {
          year: 2017,
          content: [
            {tag: "開設動態互動網頁特效入門",
            }
          ]
        }
      ]
    }
  }
  
});
《動態時間軸年表》完成視覺設計
《動態時間軸年表》完成視覺設計

以目前及時出現的效果來說其實有些粗糙,所以我們來加上一些loading時的動畫提升質感吧!這次使用Vue官方的效果Transition Group,使用方法為在HTML套用官方已寫好CSS效果的class就好囉,記得也要把語法在CSS複製貼上唷!這樣點「插入資料」的按鈕,就可以看到進場的動畫了。當然也可以搭配設計的CSS互動動畫,這部分就留給大家發揮空間、腦力激盪一下~

//HTML
#app
  button.initial(@click="initial") 插入資料
  transition-group.timeline(tag="ul",name="fade")
    li.dialog_wrapper(v-for="l in logs", :key="1")
      .dialog
        h3.year {{l.year}}
        ul.content
          li(v-for="c in l.content") {{c.tag}}
//CSS
...
.fade-enter-active, .fade-leave-active
  transition: .5s
  transform: translateY(0px)
  
.fade-enter, .fade-leave-to
  opacity: 0
  transform: translateY(50px) rotate(10deg)
《動態時間軸年表》製作動態
《動態時間軸年表》製作動態

至於一個一個排序進入的動畫,我們同時抓出物品跟現在是第幾個的id,用 transition-delay 加上秒數,動態指定動畫時間delay多久。

//HTML
li.dialog_wrapper(v-for="(l,id) in logs", :key="l", :style="{'transition-delay':id/2+'s'}")

最後撒上如巧克力米般的 deco_bar 妝點整個畫面,然後再加上下雨般的動畫,我們就大功告成啦。

//HTML
#app
  button.initial(@click="initial") 插入資料
  transition-group.timeline(tag="ul",name="fade")
    li.dialog_wrapper(v-for="(l,id) in logs", :key="l", :style="{'transition-delay':id/2+'s'}")
      .dialog
        h3.year {{l.year}}
          .decor_bar
        ul.content
          li(v-for="c in l.content") {{c.tag}}
//CSS
@keyframes rain_in
    0%
      transform: translateY(-50px)
      opacity: 0
    100%
      transform: translateY(0px)
      opacity: 1  
  
.decor_bar
    &,&:before,&:after
      content: ""
      +size(8px,30px)
      background-color: $color_yellow
      border-radius: 5px
      position: absolute
      top: -35px
      left: 30px
      animation: rain_in 0.5s 0.5s both
      
    &:before
      background-color: $color_orange
      top: -30px
      left: -20px
      animation-duration: 1s
      animation-delay: 0.5s
        
    &:after
      background-color: $color_white
      top: -60px
      left: 20px
      animation-duration: 2s
      animation-delay: 0.5s

成品請參考這邊 👉🏻 https://codepen.io/frank890417/pen/rwrZwe?editors=0010

以上就是這次可愛的時間軸年表教學,相較於其他的直播內容,這次講解到CSS相關的切版觀念,讓我們再一次複習運用到哪些重點概念。

觀念大補帖

  1. 物件相對、絕對位置關係──層層相疊的物件如何使用position來呼應位置
  2. Animation的運用──如何使用Vue transition group與撰寫CSS的keyframes
  3. CSS選取器──運用:nth-child等語法選取肚子裡的子層

只要熟悉這些概念,相信成為切版高手的路就不遠啦!那麼,我們下次見啦👋👋👋

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

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

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

墨雨設計banner

這篇文章 利用Pug(HTML)、Sass(CSS)及Vue.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 - 互動程式創作台灣站

]]>
來做SVG動畫讓蔥油餅翻滾吧! (下):讓場景中的元件動起來(直播筆記) https://creativecoding.in/2021/01/15/%e4%be%86%e5%81%9asvg%e5%8b%95%e7%95%ab%e8%ae%93%e8%94%a5%e6%b2%b9%e9%a4%85%e7%bf%bb%e6%bb%be%e5%90%a7-%ef%bc%8d%e4%b8%8b%e7%af%87%ef%bc%9a%e8%ae%93%e5%a0%b4%e6%99%af%e4%b8%ad%e7%9a%84%e5%85%83/ Fri, 15 Jan 2021 10:46:07 +0000 https://creativecoding.in/?p=469 本文翻自 [週四寫程式系列] 來做SVG動畫讓蔥油餅翻滾吧!,若是對文章內容有疑問,或是想要老闆手把手帶你飛,都可以觀看影片詳細內容。 在上一篇中,老闆帶大家從發想素材、將素材引入網頁,到製作場景進出…

這篇文章 來做SVG動畫讓蔥油餅翻滾吧! (下):讓場景中的元件動起來(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>

本文翻自 [週四寫程式系列] 來做SVG動畫讓蔥油餅翻滾吧!,若是對文章內容有疑問,或是想要老闆手把手帶你飛,都可以觀看影片詳細內容。

在上一篇中,老闆帶大家從發想素材、將素材引入網頁,到製作場景進出的動畫。接下來我們將要賦予場景中的每個元件生命。

目標

本篇文章中,老闆會帶大家完成蔥油餅小島中角色與物件的動畫,也會提示大家每個部份需要注意的地方,以下動畫的設定數值都都只是參考,開發時大家都能試試看不同的數值。

  • 實際應用﹔讓場景中的元件動起來

讓素材動起來吧

驚嘆號

首先我們從驚嘆號下手,老闆讓驚嘆號分布在場景的各處,用意是要提示使用者這邊有驚喜,引導使用者靠近這個區塊。為了增加提示效果,我們利用左右晃動的動畫效果,固定時間後會再撥放。

這邊使用了新的 css 屬性 animation ,需要搭配 @keyframes 使用,在 keyframes 用百分比去註記不同時間該顯示的樣式,透過 animation 定義動畫總時間,這也代表這個 keyframes 可以提供給不同元件使用,使用的元件賦予不同的時間等屬性,就是一個全新的樣貌。

要注意元件會以左上角為旋轉中心點,適時加上 transform-origin, transform-box 讓動畫更加合理。

[data-name="sign"]{
	// 引用動畫名稱 時間 延遲播放時間 次數
  animation: drag 2s infinite;
  transform-origin: center center;
  transform-box: fill-box;
}

@keyframes drag {
  0%{transform: rotate(0deg);}
  80%{transform: rotate(0deg);}
  85%{transform: rotate(10deg);}
  90%{transform: rotate(-6deg);}
  95%{transform: rotate(3deg);}
  100%{transform: rotate(0deg);}
}

路人 trigger_door_man

利用前面寫過的 keyframes drag 與 data-name 的概念,我們可以快速完成這個部分的動畫。當滑鼠進入感應區塊,路人能夠開始搖擺。

[data-name="trigger_door_man"]{
	&:hover{
	  animation: drag 2s infinite;
	  transform-origin: center bottom;
	  transform-box: fill-box;
	}
}
驚嘆號與路人

阿伯與跳起來的蔥油餅 trigger_cookie

要讓蔥油餅動起來之前,先從阿伯的手開始,結合蔥油餅動畫之後,讓蔥油餅宛如真的被阿伯翻了起來。撰寫這邊的動畫時,要注意調整 transform-origin ,決定動畫旋轉的參考點,阿伯的上半身也是用相同的方式處理,若是動畫沒調好,很有可能瞬間變成凶殺案,阿伯被腰斬的畫面。

透過 animation 內延遲時間設為負值,讓動畫預先開跑到其他時間點,也是讓動畫更加順暢的小訣竅。

[data-name="trigger_cookie"]{
	&:hover {
		[data-name="man_upper"]{
			animation: bake_cookies 2s infinite ;
			transform-origin: center bottom;
  		transform-box: fill-box;
		}
		[data-name="hand"]{
			// 動畫名稱 動畫時間 次數 延遲時間
			animation: drag 2s infinite -0.8s;
			transform-origin: left center;
  		transform-box: fill-box;
		}
	}
}
@keyframes bake_cookies {
	40%{
			transform: rotate(5deg);
	}
	50%{
			transform: rotate(-6deg);
	}
	70%{
			transform: rotate(-10deg);
	}
	90%{
			transform: rotate(-6deg);
	}
	100%{
			transform: rotate(5deg);
	}
}

接著我們要來處理跳起來的蔥油餅了,蔥油餅的思維比較不同,我們要讓蔥油餅分段顯示。 keyframe 的動畫內容改為使用透明度,讓透明度跟著時間改變,營造出分段顯示的效果,利用 animation-delay 來讓四個位置的蔥油餅,在不同的時間點顯示。這邊起始為置的蔥油餅會出現大部分的時間,所以要特別為它寫一個 keyframe。大家也可以試試看,調出順暢有趣的效果。

[data-name="trigger_cookie"] {
	&:hover {
		...
		[data-name="cookie1"] {
			animation: bake_cookies_locus 2s infinite -1s;
		}
		[data-name="cookie2"] {
			animation: bake_cookies_locus 2s infinite -0.8s;
		}
		[data-name="cookie3"] {
			animation: bake_cookies_locus 2s infinite -0.6s;
		}
		[data-name="cookie4"] {
			animation: bake_cookies_locus 2s infinite -0.4s;
		}
		[data-name="now_cookie"] {
			animation: now_cookie 2s infinite;
		}
	}
}
@keyframes bake_cookies {
	40% {
		transform: rotate(5deg);
	}
	50% {
		transform: rotate(-6deg);
	}
	70% {
		transform: rotate(-10deg);
	}
	90% {
		transform: rotate(-6deg);
	}
	100% {
		transform: rotate(5deg);
	}
}
@keyframes now_cookie {
	0% {
		opacity: 1;
	}
	49% {
		opacity: 1;
	}
	50% {
		opacity: 0;
	}
	90% {
		opacity: 0;
	}
	91% {
		opacity: 1;
	}
}

小孩與媽媽 trigger_cookie

不可能整個畫面都使用同樣的搖擺動態吧?所以我們為小孩換一個動態效果,讓他原地跳躍,要達成這個效果,可以改變 y 的值來達成,媽媽則是繼續搖擺效果。

[data-name="trigger_cookie"] {
	&:hover {
		[data-name="child"] {
			animation: jump 2s infinite -1s;
		}
		[data-name="mother"] {
			animation: drag 5s infinite;
			transform-origin: left bottom;
			transform-box: fill-box;
		}
	}
}
@keyframes jump {
	0%{
		transform: translateY(0px);
	}
	50%{
		transform: translateY(0px);
	}
	51%{
		transform: translateY(0px);
	}
	75%{
		transform: translateY(-10px);
	}
	100%{
		transform: translateY(0px);
	}
}
跳動的小孩與搖擺媽媽

結語

跟著操作之後,應該有 svg + css 製作動畫的概念,這時不妨動手試試看,讓其他小島的元件動起來。也可以畫些素材,加上動畫賦予他們生命力。動畫沒有什麼對或錯,可以按照自己喜好,去改變動畫效果或是調整時間,但要注意撰寫動畫時的連續繼承問題,像是阿伯的手(子層)會跟隨著身體(父層)擺動。下面老闆也提供三個議題,供大家在開發時參考。

引用 animate.css

每次都要重新寫 keyframe,若是寫 keyframe 寫累了或是遇到急案,為了加速開發,老闆這邊準備了一些補品,讓你可以快速套用現成的動畫效果。例如:想要實現當滑鼠進入感應區塊時,讓驚嘆號動畫改為閃爍動畫,在這邊我們結合 animate.css 製作閃爍動畫。引用 ainmate.css 後,當 data-name=”trigger ” hover 時,裡面 data-name=”sign”的動畫都改為 animation: flash 2s infinite。( flash 為 animate.css 的動畫名稱)

電腦與手機

由於手機等行動裝置不會有 hover 的行為,記得補上 focus,讓整個網頁能夠更完善。

了解你的使用者

把作品完成後,如果能夠知道自己這些得意之作是不是真的有被使用,可以做 GA 追蹤,老闆也因為 GA 的關係,發現其實使用者不太常使用換頁,導致這些動畫做了卻沒人看,參考數據後做了相對應的處理,增加作品露面的機會。

課程推薦

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

打好基本的互動網頁基礎之後,可以進階動畫互動網頁特效入門(JS/CANVAS),紮實掌握JavaScript 程式與動畫基礎以及進階互動,整合應用掌控資料與顯示的Vue.js前端框架,完成具有設計感的互動網站。


墨雨設計banner

訂閱 Creative Coding Taiwan 電子報:

這篇文章 來做SVG動畫讓蔥油餅翻滾吧! (下):讓場景中的元件動起來(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
資料視覺化的動態長條圖製作(上):jQuery(直播筆記) https://creativecoding.in/2021/01/06/%e8%b3%87%e6%96%99%e8%a6%96%e8%a6%ba%e5%8c%96%e7%9a%84%e5%8b%95%e6%85%8b%e9%95%b7%e6%a2%9d%e5%9c%96%e8%a3%bd%e4%bd%9c%ef%bc%88%e4%b8%8a%ef%bc%89%ef%bc%9ajquery%ef%bc%88%e7%9b%b4%e6%92%ad%e7%ad%86/ Wed, 06 Jan 2021 11:04:55 +0000 https://creativecoding.in/?p=369 資料視覺化是網頁上常用的媒材,在本篇文章中我們將實際操作在jQuery/ Vue/ D3這三種框架下如何繪製出好用的動態長條圖,同時分析各個框架的優勢與劣勢,讓我們繼續往下看吧! 首先讓我們對長條圖有…

這篇文章 資料視覺化的動態長條圖製作(上):jQuery(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
資料視覺化是網頁上常用的媒材,在本篇文章中我們將實際操作在jQuery/ Vue/ D3這三種框架下如何繪製出好用的動態長條圖,同時分析各個框架的優勢與劣勢,讓我們繼續往下看吧!

https://i.imgur.com/cEEkxZo.gif

首先讓我們對長條圖有個初步了解。長條圖多用於呈現相同性質但不同量級的資料,例如比較美國、台灣、日本這三個國家的人口數。用程式碼撰寫一個長條圖主要分為三個步驟:

  1. 資料整理,通常會用陣列表現相同性質且不斷重複的資料。
  2. 在網頁上產生相對應的物件(長條方塊)。
  3. 針對每個長條方塊設定他的屬性,例如:改變物件的高度、弧度、間距……等屬性。

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

主要會用到的知識:

  1. HTML (Pug), CSS (Sass)與JavaScript/ jQuery的基礎觀念
  2. Vue框架概念
  3. D3框架概念

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


框架一:jQuery

codepen實作範例:長條圖_jQuery

相信大家對jQuery都滿熟悉的,它的特性就是抓一個特定條件的東西,去修改他特定條件的屬性。

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

https://i.imgur.com/4krJiqp.png

接著來準備一組做為圖表資料的陣列。命名一個陣列為datas,運用JS的for迴圈,設定一個從1-10重複10遍的變數i,同時運用函數Math.random()每次推入一個隨機的變數,用console.log印出datas陣列內的資料。

https://i.imgur.com/hPNse2U.png

要產生圖表,需要製作一個放置長條圖、資料的容器,在HTML新增一個class為graph的div,在CSS針對graph的width, height, border進行屬性的設定增加可視性。

接著做進一步的資料處理,運用JS的forEach將陣列datas內的每一筆資料單獨取出(ES6簡化function為箭頭函式)給他一個參數index,console.log印出index, obj,即可看到陣列內單獨的資料和標號用的index。

https://i.imgur.com/aXgrclM.png

溫馨提醒:ES6中簡化function為箭頭函式()=>{}的寫法不是每個瀏覽器都吃,記得搭配webpack做轉換唷。

為了產生對應資料的長條圖,在forEach的迴圈內我們命名一個變數bar,bar是'<div class="bar"></div>'的HTML(注意單引號與雙引號的使用),用jQuery選取graph,利用語法append()在被選取的元素结尾插入bar的長條圖。

在CSS針對bar做width, border的屬性設定,在graph下屬性display: flex讓每個長條可以排排站。

https://i.imgur.com/gSX3fiW.png

接下來要替他們長不同的身高、上不同的顏色。首先在graph利用CSS flex的屬性align-items: flex-end讓長條方塊置底,在bar給每個長條margin-right: 10px

那要如何指定高度和顏色呢?我們利用jQuery選擇器的特性$(bar)把它命名為變數element,接著修改他CSS的height和background-color。由於obj為1-10的亂數,在高度呈現上的級距不明顯,所以把obj*20讓他的高度凸顯出來。

而CSS的顏色屬性是由rgb數值在0-255之間決定,如果只在1-10之間浮動的話會黑壓壓的一片,所以我們命名一個變數為color_val,利用語法parseInt()將obj轉成整數後再上15,這樣深淺鮮明、高度差異的長條圖就出現啦。

https://i.imgur.com/9KG7iRr.png

完成了靜態的長條圖,離動態更新長條圖就不遠了。由於接下來會比較複雜所以貼出JS的完整code給大家參考,步驟分為以下:

  1. 命名一個新的陣列為elements,用來儲存先前產生出的實體物件(變數element),利用elements.push(element)直接推進去。
  2. 利用語法setInterval()規定每500豪秒更新長條圖一次。
  3. 更新的動作利用函式generateData()來進行,把它想成一組亂數產生器,所以一樣命名一個陣列temp,把亂數塞進去。
  4. 接著讓datas = generateData();亂數更新一坨拉庫資料,有資料後就可以按照資料更新物件(長條方塊),因為剛剛把亂數產生包在陣列裡面了,所以記得先做初始化。運用forEach把每個物件抓出來玩一遍,根據對應的資料更新他的高度和背景顏色所以使用datas[index],第一筆資料對應第一個物件、第二筆資料對應第二個物件。

這樣我們就有一個會變化的長條圖了(撒花),但是否覺得變換瞬間有點卡卡的?只要在bar的CSS設定transition: 0.5s就可以解決囉!

var datas = [];
var elements = [];
function generateData(){
  var temp = [];
  for( var i=0; i<10; i++){
    temp.push(Math.random()*10+5);
  }
  return temp;
}

for( var i=0; i<10; i++){
  datas.push(Math.random()*10+5);
}
datas = generateData(); //function初始化
console.log(datas);

datas.forEach((obj, index)=>{
  console.log(index, obj);
  var bar = '<div class="bar"></div>';
  var color_val = parseInt(obj)*15;
  var element = $(bar);
  element.css("height", obj*20+"px")
         .css("background-color", "rgb("+color_val+","+color_val+","+color_val+")")
  $(".graph").append(element);
  elements.push(element);
});

//更新
setInterval(()=>{
  datas = generateData();
  elements.forEach((element,index)=>{
    var color_val = parseInt(datas[index])*15;
    element.css("height", datas[index]*20+"px")
         .css("background-color", "rgb("+color_val+","+color_val+","+color_val+")")
  });
},500);

最後一步是新增每個長條方塊所對應的數值,最快速的方法就是直接把數值包在長條bar裡面,然後使用CSS position的定位方法將數值定在長條方塊的下方。

把文字包在長條內var bar = '<div class="bar"><div class="text"></div></div>';

利用jQuery children選取element的小孩".text",用語法toFixed(1)取到小數點第1位。在下方setInterval的更新處重複ㄧ樣的步驟,記得把變數名稱obj修改成datas[index]

element.children(".text").text(obj.toFixed(1))
https://i.imgur.com/kE68ek9.png

接著在CSS針對bar和text的定位做設定,就可以看到長條圖表下方出現數值了。

.bar
  position: relative
  
.text
  position: absolute
  bottom: -30px

做到這裡你有沒有發現我們一直在做一件重複的事情?沒錯,就是針對實體物件的數值做修改。用jQuery寫圖表就有點像用一個簡單的錘子蓋房子,從建造到調整都需要針對每個DOM物件去做手動修改,土炮且自立自強。

下一篇文章中我們會講到另外兩個框架:Vue和D3,相較於如此「人工」的jQuery,這兩個框架比較偏自動化,就讓我們下次見囉~

課程推薦

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

打好基本的互動網頁基礎之後,可以進階動畫互動網頁特效入門(JS/CANVAS),紮實掌握JavaScript 程式與動畫基礎以及進階互動,整合應用掌控資料與顯示的Vue.js前端框架,完成具有設計感的互動網站。期待在課程裡見到你!

墨雨設計banner

訂閱 Creative Coding Taiwan 電子報:

這篇文章 資料視覺化的動態長條圖製作(上):jQuery(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>