在需要發想靈感的職業日常中,便利貼常常作為靈感紀錄與討論道具,有著不可取代的重要性,但困擾的是常常黏性一過,原先貼得整整齊齊的便利貼只要一有風吹草動,就如同十二月的雪一樣翩翩飛舞。如果可以把這些小傢伙電子化,豈不是美事一樁?今天我們就要用Vue.js來實作可以快速地新增、輸入內容、換色、刪除甚至拖曳編排的便利貼牆。這一次的案例因為比較複雜,所以切成兩篇來做,請上下兩篇搭配一起服用唷~

我們將以Code Pen做為本次實作的平台,這是一個可以在創作的同時即時看到程式碼運作狀況的線上程式碼編輯器,只要簡單註冊就可以使用囉!
如果想搭配直播影片一起實作,請👉🏻往這邊走
了解架構
在開始之前我們先來概念性的發想,便利貼的結構設計可分為以下這些方向:
- 儲存的資料種類—文字(顏色名稱)和色票
- 文字呈現方式—文字在便利貼上應該滿版呈現,隨著文字多寡而動態調整大小
- 拖曳功能—滑鼠點擊在便利貼上時紀錄初始座標,到結束時動態計算中間的滑動呈現
- 小功能—切換文字、刪除等功能
首先在Code Pen上開一個新的pen,將HTML的預處理器設定成Pug、CSS的預處理器設定成Sass、JS的CDN掛入Vue。

便利貼樣式與資料處理
接著我們開始從第一張便利貼刻起,便利貼的結構簡單來說就是一個裝有文字的框框,文字資料我們先暫時寫死,稍後再用Vue.js動態更新。
在HTML給他一個容器名為postit裡面裝一些文字text,接著在CSS設定他的樣式如下,如次便能得到一個文字在中間的基礎白色便利貼:
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans&display=swap')
*, *:before, *:after
border: solid 1px //之後再把邊線取消掉
font-family: 'Noto Sans', sans-serif
@mixin size($w, $h:$w)
width: $w
height: $h
$colorBlack:#3E3A39
html, body
// background-color: $colorBlack //背景色,設定樣式時先拿掉方便識別
.postit
+size(240px)
font-size: calc( 240px / 4 - 5px ) //便利貼寬÷字數-預留空隙
display: flex
justify-content: center
align-items: center

接下來替便利貼上一些活潑的顏色,讓他變得好玩一些,同時調整字的顏色跟上一些陰影來增加設計的細緻度,我們的第一張便利貼就完成了。使用的色票和陰影處理如下,大家也可以自由選擇喜歡的顏色唷:
//修改字體顏色不要太死黑 color: #44403F //便利貼顏色 $colorYellow: #FFEB67 $colorBlue: #A5D8D6 $colorRed: #EF898C $colorGreen: #CBE196 .postit background-color: $colorYellow letter-spacing: 5px font-weight: 500 box-shadow: 15px 10px 40px rgba(black,0.4)

接下來進行資料的處理,在JavaScript裡給予一組陣列名為postits,裡面裝便利貼會用到的參數text、color跟position。接著新增一個new Vue並指定作用範圍為#app,抓出物件便利貼跟他是第幾張(p,pid) in postits,將文字套進{{p.text}}。用左下角的Console檢查資料是否連接好,先輸入vm之後,再輸入vm.$data.postits[0].text="temp"資料,如果出現”temp”替換掉原先的文字「都市更新」就代表成功。
//Vue.js
var vm = new Vue({
el: '#app',
data: {
postits: [
{
text: "都市更新",
color: "yellow",
pos: {x:20, y:0}
}
]
}
})

加上滑鼠互動事件
為了增加自由度,我們來替便利貼加上拖曳的功能。拖曳代表著我們必須在螢幕上做絕對定位,當在拖動物件時,左上角的距離不斷地重複更新,要做到這項事情,首先我們必須將資料與定位綁定起來。
在CSS的.postit裡加上position: absolute,有時在Vue我們會動態地加上style,但這容易造成HTML程式碼裡拖了一長串反而不好閱讀,所以像這種共用性高的style可以考慮直接在CSS做設定。
接著在JS設定style的[methods](<https://cythilya.github.io/2017/04/17/vue-methods-and-event-handling/>)如下,然後帶入HTML的postit後,可在Console透過剛剛上面的方法改變文字來測試字型大小的調整是否成功。
methods:{
postitCss(p){
return {
left: p.pos.x+"px", //動態地更新便利貼的位置
top: p.pos.y+"px",
'font-size': ((240-30) / p.text.length) +'px' //根據文字長度動態設定大小
}
}
}

顏色的設定跟文字的方法差不多,在JS的data裡面新增一個名為colorList的陣列,裡面塞入我們剛剛的色票與相對應的名字:
colorList: [
{
name:"yellow",
color: "#FFEB67"
},{
name:"blue",
color: "#A5D8D6"
},{
name:"red",
color: "#EF898C"
},{
name:"green",
color: "#CBE196"
},{
name:"black",
color: "#3E3A39"
}
],
接著在下方的methods裡return他的值。在Vue裡面你要抓他的值可以直接用this指向,定義條件用find過濾符合的資料。
//JavaScript 'background-color': this.colorList.find(o=>o.name==p.color).color
這時我們可以製作一個control pannel來快速調整便利貼內的文字跟顏色,省去一直打開Console輸入指令測試的重複步驟。在HTML新增一個ul放入li和輸入欄位input,input分別對應到p.text抓取輸入文字內容和p.color選擇便利貼顏色,再設定他的css。
//HTML
ul.datalist
li(v-for="(p,pid) in postits")
input(v-model="p.text")
input(v-model="p.color")
//css .datalist position: fixed right: 20px top: 20px width: 30%

前面做了資料和定位的綁定後滑鼠靜止的部分搞定,接下來要做滑鼠移動時的行為,這部分比較複雜可能需要多一點時間理解唷。我們在整個畫面上紀錄滑鼠移動的位置並儲存在evt內,右鍵檢查裡面有一個參數offset代表滑鼠距離左上角的相對位置。
小筆記:evt代表event,每次滑鼠移動時會觸發的一連串事件,包括滑鼠位置、點擊放開等,都會儲存在這裏面。
//JavaScript
window.onmousemove = (evt)=>{
//滑鼠移動時,將最新位置記錄到vue中
// console.log(evt)
vm.postits[0].pos.x=evt.pageX //設定第一張便利貼的x距等於滑鼠在頁面上的x距
vm.postits[0].pos.y=evt.pageY //設定第一張便利貼的y距等於滑鼠在頁面上的y距
}

現在便利貼會跟著滑鼠移動,但我們希望移動是滑鼠點擊觸發之後才發生的。所以需要在data的地方多儲存一個nowId: -1。nowId代表滑鼠點擊但還沒放開時,滑鼠在哪一張便利貼的範圍裡面。
我們在HTML裡加上@mousedown="selectId(pid)"並綁定點擊事件放在methods裡面,新增滑鼠移開的事件。為了方便識別可以把nowId印在畫面上。
//JavaScript
var vm = new Vue({
...
data: {
...
nowId: -1
},
methods: {
...
selectId(id){
console.log(id)
this.nowId=id //滑鼠點擊時選擇該張便利貼
}
}
})
//滑鼠移動時,將最新位置記錄到vue中
window.onmousemove = (evt)=>{
// console.log(evt)
if (vm.nowId!= -1){
vm.postits[vm.nowId].pos.x=evt.pageX //抓取第nowId張便利貼的x距等於滑鼠在頁面上的x距
vm.postits[vm.nowId].pos.y=evt.pageY //抓取第nowId張便利貼的y距等於滑鼠在頁面上的y距
}
}
window.onmouseup = (evt)=>{
vm.nowId = -1 //滑鼠未點擊時沒有選擇任何便利貼
}
//HTML
#app
.postit(v-for="(p,pid) in postits",
:style="postitCss(p)",
@mousedown="selectId(pid)")
.text {{p.text}}
ul.datalist
//把nowId印在畫面上
li
h1(style="color: white") {{nowId}}
...

單用window.onmousemove = (evt)監測時可能會有些狀況,我們希望當滑鼠有變動時,就在Vue裡偵測資料的變動並針對位置做更新,所以我們新增watch偵測是否有選擇便利貼,如果有就把這張便利貼抓出來。這張便利貼會等於所有便利貼的第nowId個,知道第幾張後就去設定他的位置。
var vm = new Vue({
data: {
...
mousePos: {
x:0, y:0
}
},
watch: {
mousePos(){
if (this.nowId!= -1){
let nowPostit = this.postits[this.nowId]
nowPostit.pos.x = this.mousePos.x
nowPostit.pos.y = this.mousePos.y
}
console.log(this.mousePos)
},
...
})
window.onmousemove = (evt)=>{
// console.log(evt)
vm.mousePos = {x: evt.pageX, y: evt.pageY}
//if (vm.nowId!= -1){
//vm.postits[vm.nowId].pos.x=evt.pageX
//vm.postits[vm.nowId].pos.y=evt.pageY
//}
}
記得把作用範圍撐開跟window一樣大,不然會無法運作。
//css html, body, #app background-color: $colorBlack padding: 0 margin: 0 overflow: hidden +size(100%)
拖曳的功能就完成了,但這時點擊便利貼的右下角時會有一些偏移、跳一下,我們可以記錄這個偏移量並加上點擊的位置,這樣一減一加之後我們就可以讓他乖乖待在位置上。在HTML的selectId新增$event,也記錄第一個點下去的位置startMousePos。
@mousedown="selectId($event,pid)"
var vm =new Vue({
...
data: {
...
startMousePos: {
x: 0,
y: 0
}
}
...
methods: {
...
selectId(evt,id){
console.log(id)
this.nowId=id //滑鼠點下去
this.startMousePos = {
x: evt.offsetX,
y: evt.offsetY
}
}
}
})

在watch減掉偏移量,有滑鼠移動而且我們判斷當下那張便利貼存在的時候。這樣拖曳功能就大功告成啦。
watch: {
mousePos(){
if (this.nowId!= -1){
let nowPostit = this.postits[this.nowId]
nowPostit.pos.x = this.mousePos.x-this.startMousePos.x
nowPostit.pos.y = this.mousePos.y-this.startMousePos.y
}
console.log(this.mousePos)
}
如果想要將滑鼠拖曳的效果變得更滑順,可以在CSS的.postit裡面加上cursor: pointer試試效果。
新增與修改顏色
接下來我們來做「新增」的功能,做一個button點擊觸發functionaddPostits。在addPostits推入新的陣列,位置給予亂數Math.random才不會覆蓋在原有的便利貼上。

//HTML
ul.datalist
...
button(@click="addPostits") +新增便利貼
最後做個改顏色的功能,透過點擊動態渲染的小方塊來改變便利貼的顏色,參考如下。
ul.datalist
li
...
.colorList
.block(v-for="color in colorList",:style="{backgroundColor: color.color}", @click="p.color=color.name")
//css .block +size(30px) background-color: #fff display: inline-block
//JavaScript
methods: {
//直接從現有顏色清單裡選取一顏色
getColor(name){
return this.colorList.find( o=>o.name==name)
}
...
}

總結
以上就是前半段的便利貼教學,我們先建立便利貼的基礎,從樣式雛形到基本資料處理,可以分為以下幾個重點:
1. 便利貼樣式與資料處理 – 建立便利貼架構,色票、文字樣式和文字位置
2. 加上滑鼠互動事件 – 紀錄滑鼠移動的位置並儲存在evt,運用nowId判斷滑鼠在哪一張便利貼的範圍裡面
3. 新增便利貼與修改顏色 – 做一個button點擊觸發function addPostits,在addPostits推入新的陣列
下一次我們會接續進行刪除、背景調整、縮放動畫等的功能,我們下次見啦👋👋👋
老闆的互動網頁課程
動畫互動網頁程式入門(HTML/CSS/JS)以簡單例子帶你入門網站的基礎架構及開發,用素材刻出簡單有趣又美觀的網頁和動畫,享受做出獨一無二的網頁所帶來的成就感,在職場上與設計師和工程師合作無間。
打好基本的互動網頁基礎之後,可以進階動畫互動網頁特效入門(JS/CANVAS),紮實掌握JavaScript 程式與動畫基礎以及進階互動,整合應用掌控資料與顯示的Vue.js前端框架,完成具有設計感的互動網站。期待在課程裡見到你!
此篇直播筆記由幫手 Jeudi Kuo 協助整理




