SVG 彙整 | Creative Coding TW - 互動程式創作台灣站 https://creativecoding.in/tag/svg/ 蒐集互動設計案例、教學與業界資源,幫助你一起進入互動程式創作的產業 Thu, 03 Jun 2021 06:41:55 +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 SVG 彙整 | Creative Coding TW - 互動程式創作台灣站 https://creativecoding.in/tag/svg/ 32 32 來做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 - 互動程式創作台灣站

]]>
來做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%8a%e7%af%87%ef%bc%88%e7%9b%b4%e6%92%ad%e7%ad%86%e8%a8%98%ef%bc%89/ Fri, 15 Jan 2021 10:34:05 +0000 https://creativecoding.in/?p=461 本文翻自 [週四寫程式系列] 來做SVG動畫讓蔥油餅翻滾吧!,若是對文章內容有疑問,或是想要老闆手把手帶你飛,都可以觀看影片詳細內容。 今天老闆要以曾經接手的 台北聲音地景計畫 做為案例,跟大家分享從…

這篇文章 來做SVG動畫讓蔥油餅翻滾吧! (上):發想素材、將素材引入網頁(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>

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

今天老闆要以曾經接手的 台北聲音地景計畫 做為案例,跟大家分享從專案發想、素材準備到 svg 網頁動畫。當初這個專案是做為捷運站音樂的競賽網站,希望透過聲音做為媒介,連結城市的生活空間。客戶為這個專案準備許多環境音素材,提供比賽使用,但僅是把聲音素材提供給參賽者,讓老闆覺得非常的可惜,該如何將這些素材效益最大化,聲音又要如何跟場景的記憶關聯起來,把故事跟企畫更加完善。最後老闆決定結合網頁動畫與聲音,讓使用者觀看網頁時,透過與現場環境的圖象互動,觸發聲音的回饋,讓使用者將環境與聲音做連結,於是有了這樣的專案產生。

目標

本篇文章中,老闆會帶大家完成元件的創作,並在網頁中將元件導入,完成基本的動畫操作,作為下一篇文章的暖身。

  • isometric 創作場景
  • 使用 vue, ajax 將 svg 放進網頁
  • 小試身手﹔操作 svg 元件,賦予簡單的互動與動態

素材準備

有許多方式和工具能夠製作網頁動態畫面,舉凡 css、svg、canvas、Pixi.js 等,但不管哪一種方式製作動畫,場景和演員是不可或缺的,接下來讓老闆分享動畫要使用到的素材發想過程,想看老闆怎麼完成素材,可以跟著前面提供的影片連結,和老闆一起動手操作。

創作工具

Adobe Illustrator (可到官網上選擇免費版,若是常使用也可選擇付費版)

素材收集與發想

這個專案的困難點,是因為捷運站眾多,創作前,需要到各場域中收集足夠的場景資料,才有足夠的資料來作為創作的依據。

引用自電獺少女

在風格方面,老闆選擇以 isometric 作為創作風格,等距視角(isometric)風格也稱為等軸測圖。採用45度視角,沒有消失點,達到平面上模擬 3D 效果,讓人產生有深度的錯覺。最有名的代表遊戲就是紀念碑谷,若是想知道更多相關設計,也可以到 Pinterest 上查詢相關風格的作品。

創作 isometric 作品的時候,可以利用等距網格輔助創作。

等距網格

用 Illustrator 創作時,記得要幫元件群組與命名,讓開發時,能夠快速選取目標套上動畫。

起手式 – 環境與素材載入

環境

影片中老闆是使用 codepen 來做為開發環境,好處是可以直接引入需要的工具,但載入素材時會遇到跨域問題要解決。這邊提供大家在本機開發的方式。

  1. 將開發需要用到的圖片整理到專案資料夾中
  2. 開始撰寫 html ,載入 CDN,這邊會使用到的 CDN 有兩隻,目的分別如下
    • jQuery – 負責將素材導入
    • vue.js -資料綁定畫面

資源載入

完成 html 結構且載入 CDN 之後,我們要準備讓 svg 動起來,這邊也獻上素材,提供給沒有素材的開發者們,可以直接進入開發。

這邊先解說 script 的內容,我們使用 Vue 來做為資料與畫面綁定的套件。宣告 Vue 的方式如下, el 為要將資料綁定在哪個區塊的畫面中,這裡填寫 #app,也就是到時資料會被綁定在 id=”app” 的 div 中。data 的內容為需要綁定的資料。mounted 為 Lifecycle 中的其中一個鉤子,代表已經抓到渲染的對象,且資料已經成功綁定,這個階段就是載入外部素材的最佳時刻,確保我們的導入的資料能夠成功綁定在對應的位置。

在 Vue 中,從元件初始化到註銷的過程中有許多 hook (created, mounted, destroyed…),提供使用者在元件不同階段中,能夠執行不同工作。詳細內容可以參考 官方文件

使用 jQuery 抓資料的方式如以下程式碼,這邊要小心 this 的使用,如果在外面沒有先宣告 vobj 把 this 記下來,直接寫 this.svg = red 會抓不到外層 vue 的實體資料。

var vm = new Vue({
	// 綁定的範圍
	el: '#app',
	// 綁定的資料
  data: {
		scene_door: ''
	},
	// vue 生命函數,資料載入完成後就執行
	mounted() {
		var vobj = this
		$.get('./imgs/scene.svg', function (res) {
			vobj.scene_door= res
		}, 'text')
	}
})

接著要把素材導入畫面了,這邊我們先在 <body></body> 中加入以下程式碼,id=”app” 是給 vue 辨識綁定畫面的位置。這邊可以看到 v-html=”scene_door” ,v-html 是 vue 的模板與法,會將 data 內的 scene_door 用 html 的方式導入。

在實務中,要小心使用,確定是安全的資料才渲染出來,避免使用者塞入惡意程式碼,但因為此專案是使用我們自己製作的內容,不用擔心被惡意攻擊。

<body>
	<div id="app">
		<div class="scene" v-html="scene_door"></div>
	</div>
</body>

成功之後我們會看到, svg 已經備載入到網頁中了,也可以撰寫 css 將畫面置中。

圖片成功引入

先備知識

接下來,藉由以下兩個目標,讓大家熟悉這個專案會頻繁使用到的概念,1. css 選擇器 2. transition

灰色框與神秘的互動區塊

在畫面中可以看到許多灰色的外框,這是老闆設計要來做為使用者觸發動畫偵測範圍。但是總不能讓這些灰框直接在正式專案上顯示吧?這時就得感謝先前在製作素材時,貼心的我們了。若是在製作素材時,有勤勞的為圖層命名,匯出時會提供選項讓大家選擇,現在我們就能直接在寫 css 使用 data-name 的方式去控制對應元件了。

為什麼不使用 id 呢?因為 id 名稱一個網頁只能有一個,為了避免日後撞名字的問題,這邊選擇使用 data-name。

我們使用開發者工具檢查時,可以觀察到老闆提供的素材中,灰框的 data-name 屬性值為 hidden,我們可以在 scss 中加入一些語法將它變成透明的,但是只把它透明度變成0還不夠,還必須幫它加上背景顏色,並在 trigger 加上改變滑鼠游標的語法。讓使用者滑入互動區塊時,藉由滑鼠的改變,提示使用者此區塊是可以互動的。

// 擁有 data-name 屬性,且該屬性的值為 hidden
[data-name='hidden'] {
	opacity: 0;
	stroke: transparent;
  fill: transparent;
}
// 擁有 data-name 屬性,且該屬性開頭的值為 trigger
[data-name*='trigger'] {
	cursor: pointer;
}
觀察滑鼠進出感應區塊的狀態

做個開關模擬網頁剛進入的狀況

畫面的感應區塊完成後,我們接著來製作開關模擬使用者剛進入網頁的狀況。首先先在 data 中新增一個新的變數,並綁定到畫面中的 input 。大家如果想觀察 vue 資料的變化,也可以去下載套件 vue devtool 輔助開發。透過 input 做為開關之後,場景也必須要跟著互動,這邊我們先使用 v-model 綁定資料,並用 v-bind 來綁定場景 class,當監聽到 trigger 為 true 時,程式會動態的幫我們在 .scene 加上 .active。

var vm = new Vue({
	...
	data: {
		trigger: true,
		scene_door: ''
	},
	...
})
<div id="app">
	<!-- v-model 綁定資料 trigger-->
	<input id="trigger" v-model="trigger" type="checkbox">
	<label for="trigger">場景開啟</label>
	<!-- v-bind 動態改變 class -->
	<div class="scene" v-html="scene_door" v-bind:class="trigger? 'active': ''"></div>
</div>

接下來要讓開關與動畫結合,我們希望當打開開關時,三個小島會有時間差的進入場景,這裡我們又再次使用到前面的技巧,使用 data-name 將所有 island 開頭的內容透明度改成0,在外層吃到 active 後輪流進場。要注意的是,svg 不吃 top 屬性,所以這邊我們改使用 translateY 來讓小島有浮起來的效果。透過 transition-delay 設定不同秒數,達成輪流進場的效果。svg 支援的 css 屬性可參考這篇文章。使用到 transition, transition-delay 也可以到 w3c 中深入了解

.active {
    [data-name*="island"] {
        opacity: 1;
        transform: translateY(0px);
    }
}

[data-name*="island"] {
    opacity: 0;
		// 補間動畫屬性 時間
    transition: all .5s;
		// 初始位置往下 30px
    transform: translateY(30px);
}
[data-name*="island_1"] {
    transition-delay: .1s;
}
[data-name*="island_2"] {
    transition-delay: .3s;
}
[data-name*="island_3"] {
    transition-delay: .5s;
}
  • 做個開關模擬剛進入畫面的狀況
div {
	// 補間動畫 屬性 時間
	transition: all .2s;
	// 動畫,要與 @keyframe 搭配,動畫名稱 動畫時間 延遲時間 次數
	animation: animationName 2s -0.2s infinite;
	// 為了避免動畫走鐘,加上動畫時可以設定動畫的中心點,也可以寫成百分比。
	transform-orign: center center;
	
	// 滑鼠經過時觸發的內容
	&:hover { ... }
	// 行動裝置不會有 hover, 改使用為 focus
	&:focus { ... }
}

@keyframes animationName {
 // 在整段時間的 0% 要做什麼動畫
	0% { ... }
	50% { ... }
	100% { ... }
}

結語

藉由以上的操作,相信大家已經能夠利用 vue, svg 製作簡易的動畫了,下一篇老闆將帶大家逐一將場景中的動畫實現,也會與大家分享每個區塊動畫的製作思維。

課程推薦

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

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

墨雨設計banner

訂閱 Creative Coding Taiwan 電子報:

這篇文章 來做SVG動畫讓蔥油餅翻滾吧! (上):發想素材、將素材引入網頁(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
讓我們來做個互動天氣地圖吧!(直播筆記) https://creativecoding.in/2020/03/28/%e8%ae%93%e6%88%91%e5%80%91%e4%be%86%e5%81%9a%e5%80%8b%e4%ba%92%e5%8b%95%e5%a4%a9%e6%b0%a3%e5%9c%b0%e5%9c%96%e5%90%a7%ef%bc%81%ef%bc%88%e7%9b%b4%e6%92%ad%e7%ad%86%e8%a8%98%ef%bc%89/ Fri, 27 Mar 2020 19:15:54 +0000 https://creativecoding.in/?p=294 這次的直播要做一個互動地圖,當滑鼠滑過的時候顯示當地天氣資訊! 這次的直播內容主要有幾個部分: 取得地圖的 svg 的檔案,並修改成我們可以用的檔案類型。 取得台灣的地圖資料,包含行政區域名稱與代號,…

這篇文章 讓我們來做個互動天氣地圖吧!(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>
這次的直播要做一個互動地圖,當滑鼠滑過的時候顯示當地天氣資訊!

這次的直播內容主要有幾個部分:

  1. 取得地圖的 svg 的檔案,並修改成我們可以用的檔案類型。
  2. 取得台灣的地圖資料,包含行政區域名稱與代號,並把資料對應到地圖上。
  3. 做出互動的頁面,包含滑鼠滑過時的移動、變色,還有右側的資料顯示。

筆記會著重在 3. 程式實作的部分,並把相關的資料與圖片連同程式碼一起放在 github 上面,供大家參考💻。

要跟著影片一起做也沒問題:


程式實作

主要會用到的工具與知識:

  1. HTML, CSS 與 JavaScript 的基礎觀念
  2. svg 的基本操作
  3. Vue.js 框架的操作
  4. 使用 axios 串接中央氣象局 open data API

讀取地圖,並操作樣式

要在我們的頁面讀取 svg 有幾種方法,使用 <img> tag 讀取檔案,或是直接把 <svg> 包覆的內容直接貼在 html 裡面,但是如果直接讀取檔案的話就沒有辦法對 svg 進行操作,所以我們選擇後者。

svg 檔案的格式跟 html 很像,都是一層一層的往下排列,不過相對於 html 裡面的內容是 <html> 包覆所有物件,svg 則是在 <svg> 包覆繪圖軟體裡面使用的「群組」<g>、「路徑」<path> 、「線段」<line> 或是 <ploygon><circle> 圖形等元件。

讀進 svg 的 html 大概會長這樣:

<html>

<head>
...
</head>

<body>
    <svg data-name="圖層 1" xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 595.28 841.89">
        <defs />
        <title>
            image.svg
        </title>
        <path id="161d ... 09.27h0Z" />
        ...
        <path id="41139c2a ... 1.22-1.29Z" />
    </svg>
</body>

</html>

我們先在 CSS 加上一些樣式與高度的限制,才不會讓圖片太大。另外把 svg 裡面的 path 元件加上顏色跟滑鼠移過(hover)時的變化。

這邊要注意,用 css 操作 svg 顏色的方式跟一般 dom 元件的方式不太一樣,如果要改變 path 的填色,不能用 background,而是要用 fill,線條的話不是 border,而是 stroke 喔!想要知道更多 svg 的屬性與使用方式可以參考這個 CSS-TRICK 的整理:SVG Properties and CSS

:root {
    --color-gold: #B99362;
}

body {
    background-color: #222
}

svg {
    height: 100vh;
}


path {
    stroke: white;
    fill: transparent;
    transition: 0.5s;
    cursor: pointer;
}

path:hover {
    fill: var(--color-gold);
    transform: translate(-5px, -5px);
}

到這邊,我們就有一個 hover 時會變色與區域浮起來的地圖囉!

篩選滑鼠移動過地區的地理資料

緊接著我們需要把地圖畫面跟資料連在一起,當滑動過某個地區的時候,要先知道現在滑鼠在哪一個縣市,才有辦法在畫面上顯示相對應的地理資訊~

我們先觀察一下 svg 裡面各個縣市 <path> 的結構,以台北市為例:

<path id="1e48e0bb-8964-4121-b347-b900162cf771" data-name="taipei_city" class="96fdfe13-4732-40bb-9e9c-cdc6e310fcb9" d="M466.27,77.17,465.42,79l-.85.85-.24.49-.85,1v1.83l-1.22.73L462,85.47l.49,1.59,1.22.85,3.9.49,2.44,2.32,1,1.83.12,5.61-1.83,2.56-1.22,1.1-.61,1.34.37,3.54.73,1.46,1.46-.12,1.34-.73.85-1.22.85-.12,2,.85,1.1.85.49,1.34v1.83l.85,1.46,1.58,1.1,1.71.12.73,2.93,2.56,1.71,6.83.73,1.46-.61h2l.12-1.22-3.29-1.59-.24-1.71.12-1.46-.85-2.8v-1.58l.85-1.34,1.22-.73,3.41.24,1.1-.73,1.46-.37,4.63.24.37-1-1.1-.49-1.58.49L497.86,103l-3.29-2.44L494,99.25l.85-1.1-.24-1.34-1-1,.73-.85,1.59-3.29-.37-3.17L490.29,84l.37-1.59,1-1.1-.37-1.22-2-1.71-.73-1.1-.12-3.54-2-2V70l.49-1.34V66.81l-3.17.24-1.34-2.44-1-1.1-1.71,2.68-1.34.61-.61,1.34-2.56,1.46-.61,1.59-1.1.73-1.71.12-1.34.61-2.07,2.44-.61,1.59-1.59.48h0Z" />

發現其實可以直接在 <path> 裡面加上自己定義的 data-* attribute,如此一來,就可以用類似 jQuery 的 attr() 方法,甚至是透過 DOM element 的 data 屬性取得這個名字的內容。

舉例而言,我們可以在直接在瀏覽器加上某個縣市的 onmouseover 監聽器,並在滑鼠移動的時候印出地圖位置的 data-name 的值,就可以這樣做:

// 使用原生 JavaScript 的寫法
// 先抓取台北市的 path 物件
const el_taipei_city = document.getElementById('1e48e0bb-8964-4121-b347-b900162cf771')
// 加上監聽器,打印出我們要的 data-* attribute 內容
el_taipei_city.onmouseover = function({console.log(this.dataset.name)}
// 結果 -> taipei_city

有了地圖的資訊之後,我們只要拿著這個地點的名稱去比對現有的天氣資料,就可以把相對應的資料放到畫面上了。地理資訊的資料型態長這樣:

var place_data=[
  {
   tag: "taipei_city",
   place: "臺北市",
   low: 16,
   high: 24,
   weather: "Rainy"
  },
  {
   tag: "new_taipei_city",
   place: "新北市",
   low: 15,
   high: 22,
   weather: "Rainy"
  }
  ...
]

假設拿著 taipei_city 字串,想要從 place_data 取得整個台北市的物件要怎麼做呢? 我們可以用 for 迴圈,一個一個比對,但是其實 JavaScript 有提供我們更簡潔的寫法,就是陣列的 filter 方法,我們可以直接回傳陣列裡面中,判斷函式結果為 true 的物件。

像是這樣:Do re mi so ~

current_place_obj = place_data.filter((obj)=>obj.tag === 'taipei_city')[0]

因此,我們可以很快速的獲得 tag 是 taipei_city 的整個物件,要小心 filter 方法回傳的也是一個陣列,所以如果要取值的話,要取第0項喔。

將資料用 Vue 綁定,並渲染在畫面上

這邊比較需要注意的地方有兩個,第一個是需要在組件 mounted 的時候把所有的 <path> tag 加上滑鼠移動的監聽器,當滑鼠移動的時候,就更新當前選擇到的 data-name 屬性更新到組件的 filter 資料上。第二個是可以利用 Vue 的 computed 計算屬性即時根據 filter 的變動即時更新要顯示當前資料的物件 now_area

const app = new Vue({
    el: '#app',
    mounted() {
        paths = document.querySelectorAll('path');
        let _this = this // 把這個 vm 本身存在 _this,以供後續函式內部使用
        paths.forEach(e => {
            e.onmouseover = function () {
                _this.filter = this.dataset.name
            }
        })
    },
    data: () => {
        return {
            filter: '',
            place_data: null,
        }
    },
    computed: {
        now_area() {
            let result = place_data.filter((obj) => obj.tag === this.filter)
            if (result.length == 0) {
                return null
            } else {
                return result[0]
            }
        }
    },
})

最後快速的加上標題與內容的樣式,就完成這次的作品了!🎉🎉🎉

加碼小單元-串接中央氣象局的 API 顯示實際的溫度與氣象

我們選擇使用中央氣象局氣象資料開放平台提供的資料,先註冊帳號後到這個網址:https://opendata.cwb.gov.tw/user/authkey,點擊下圖中的取得授權碼之後右邊就會出現你的 API token,要好好保管他這份資料呦。之後只要使用這個 token 就可以通行無阻的拿到我們需要的資料了~

開發指南可以看到呼叫 API 的規範大致上是長這樣:

※ URL: https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/{dataid}?Authorization={apikey}&format={format}
                
{dataid} 為各資料集代碼 (參照:資料清單)  ex.F-A0012-001
                
{apikey} 為會員帳號對應之授權碼  ex.CWB-1234ABCD-78EF-GH90-12XY-IJKL12345678
                
{format} 為資料格式,請參照各資料集頁面確認可下載之檔案格式  ex.XML、CAP、JSON、ZIP、KMZ、GRIB2
                
※ 範例:https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/F-A0012-001?Authorization=CWB-1234ABCD-78EF-GH90-12XY-IJKL12345678&format=XML
                
並請加入快取功能,如上述所示。

因為我們需要的是以JSON格式顯現的鄉鎮天氣預報-台灣未來1週天氣預報,因此呼叫的規格大概是這樣: let url = 'https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/F-D0047-091?Authorization=你的API_token&downloadType=WEB&format=JSON'

我們使用 axios 的 get 方法拿取資料看看 axios.get(url).then(data => {console.log(data)}),可以拿到這個樣子的資料,打開之後發現我們需要的就是在 dataset -> locations 裡面的 location 陣列資料。

再往下看可以看出他的結構,locationName 是縣市名稱,descriptionName 是這個數值的名稱,這才發現,原來氣象局的資料還會依照時段區分,我們尋求最簡單的作法,直接抓離現在最近的時段就好。

weatherElement 裡面有很多資料提供的數值,如果不清楚意思的話也可以查閱檔案的欄位說明表,雖然文件通常又臭又長,但是好好讀一下都能省下不少開發的時間。最複雜的解析部分已經完成,接下來只要把資料源跟篩選方式改成從api抓回來的資料就可以了!

接下來先把 call 到的資料存到 data 的 weather_data 中,另外把 filter 方法換成 find,因為我們只需要第一個符合條件的結果。把回傳的資料格式修整一下,符合原本的資料型態就可以直接呈現囉~

mounted() {
    axios.get(url).then(data => {
        console.log(data)
        this.weather_data = data.data.cwbopendata.dataset.locations.location
    })
...
now_area() {
    let data = {}
    let result = this.weather_data.find((obj) => {
        return obj.locationName === this.filter
    })
    
    if (result) {
        let high = result.weatherElement.find(el => el.elementName === 'MaxT').time[0].elementValue.value
        let low = result.weatherElement.find(el => el.elementName === 'MinT').time[0].elementValue.value
        let weather = result.weatherElement.find(el => el.elementName === 'Wx').time[0].elementValue[0].value
        data = {
            place: this.filter,
            low: low,
            high: high,
            weather: weather
        }
    }
    return data
}

這樣就大功告成了!專案的原始碼可以在這邊查看:https://github.com/Monoame-Design/bosscoding-examples

墨雨設計banner

訂閱 Creative Coding Taiwan 電子報:

這篇文章 讓我們來做個互動天氣地圖吧!(直播筆記) 最早出現於 Creative Coding TW - 互動程式創作台灣站

]]>