本系列文已改編成書「Arduino 自造趣:結合 JavaScript x Vue x Phaser 輕鬆打造個人遊戲機」,本書改用 Vue3 與 TypeScript 全面重構且加上更詳細的說明,
在此感謝 iT 邦幫忙、博碩文化與編輯小 p 的協助,歡迎大家前往購書,鱈魚在此感謝大家 (。・∀・)。
若想 DIY 卻不知道零件去哪裡買的讀者,可以參考此連結 。( •̀ ω •́ )✧
這個章節來實作彩球組件,讓使用者可以選擇顏色。
建立 color-ball.vue
組件。
src\components\window-app-rgb-led-palette\color-ball.vue
<template lang="pug">
.color-ball-section
.color-ball
</template>
<style lang="sass">
@import '@/styles/quasar.variables.sass'
.color-ball-section
position: absolute
padding: 30px
width: 100%
height: 100%
display: flex
justify-content: center
align-items: center
.color-ball
$size: 180px
width: $size
height: $size
box-shadow: 0px 0px 20px rgba(black, 0.1)
border-radius: 99999px
transition: background 0s, transform 0.4s
&:hover
transition-duration: 0.2s
transform: scale(1.04)
transition-timing-function: cubic-bezier(0.385, 1.055, 0.465, 1.190)
&:active
transition-duration: 0.2s
transform: scale(1)
transition-timing-function: cubic-bezier(0.385, 1.055, 0.535, 1.570)
</style>
<script>
export default {
name: 'ColorBall',
components: {},
props: {},
data() {
return {};
},
computed: {},
watch: {},
created() {},
mounted() {},
methods: {},
};
</script>
接著在 window-app-rgb-led-palette.vue
引入 color-ball.vue
。
src\components\window-app-rgb-led-palette\window-app-rgb-led-palette.vue <script>
// ...
import BaseWindow from '@/components/base-window.vue';
import BaseSelectPin from '@/components/base-select-pin.vue';
import ColorBall from '@/components/window-app-rgb-led-palette/color-ball.vue';
// ...
export default {
name: 'WindowAppRgbLedPalette',
components: {
'base-window': BaseWindow,
'base-select-pin': BaseSelectPin,
'color-ball': ColorBall,
},
// ...
};
src\components\window-app-rgb-led-palette\window-app-rgb-led-palette.vue <template lang="pug">
base-window.window-app-rgb-led-palette(
// ...
)
.h-full.overflow-hidden
transition(name='switch-right')
color-ball(v-if='isSettingOk', v-model='colorVal')
// ...
球球出現了!
接下來依序完成 color-ball.vue
功能,需求為:
v-model
Quasar Color Picker
src\components\window-app-rgb-led-palette\color-ball.vue <script>
export default {
name: 'ColorBall',
components: {},
props: {
value: {
type: String,
default: '',
},
},
data() {
return {
colorVal: '',
};
},
computed: {
/** 提供 background 顏色 */
style() {
return {
background: this.colorVal,
};
},
},
watch: {
/** 偵測選擇變化,透過 emit('input') 更新 v-model 數值 */
colorVal(val) {
this.$emit('input', val);
},
},
created() {
// 儲存初始顏色
this.colorVal = this.value;
},
mounted() {},
methods: {},
};
src\components\window-app-rgb-led-palette\color-ball.vue <template lang="pug">
.color-ball-section
// 使用 q-menu 提供彈出選單功能
q-menu(anchor='center end', self='center start')
q-color(v-model='colorVal')
// 綁定 style,變更 background 顏色
.color-ball(:style='style')
實測功能。
成功變色!最後讓我們加入色環。
實作以下功能:
src\components\window-app-rgb-led-palette\color-ball.vue <script>
import { colors } from 'quasar';
const { hexToRgb } = colors;
export default {
name: 'ColorBall',
// ...
computed: {
// ...
/** 根據 colorVal 計算之 RGB 數值
* 使用 Quasar 提供之 Color Utils 達成
* https://v1.quasar.dev/quasar-utils/color-utils
*/
rgbVal() {
if (this.colorVal === '') {
return {
r: 0,
g: 0,
b: 0,
};
}
return hexToRgb(this.colorVal);
},
},
// ...
};
src\components\window-app-rgb-led-palette\color-ball.vue <template lang="pug">
.color-ball-section
// 使用 q-menu 提供彈出選單功能
q-menu(anchor='center end', self='center start')
q-color(v-model='colorVal')
q-knob(
v-model='rgbVal.r',
color='red-4',
size='220px',
:thickness='0.05',
readonly,
:angle='0',
:min='0',
:max='765'
)
q-knob(
v-model='rgbVal.g',
color='green-4',
size='220px',
:thickness='0.05',
readonly,
:angle='120',
:min='0',
:max='765'
)
q-knob(
v-model='rgbVal.b',
color='blue-4',
size='220px',
:thickness='0.05',
readonly,
:angle='240',
:min='0',
:max='765'
)
// 綁定 style,變更 background 顏色
.color-ball(:style='style')
knob 之
max
設為 765,是因為 RGB 最大值為 255,設 765 剛好可以讓每個 knob 各占 3 分之 1。
看看效果如何。
感覺真不錯!(ง •̀_•́)ง
最後就是實際讓 RGB LED 發亮了!讓我們回到 window-app-rgb-led-palette.vue
功能需求為:
colorVal
16 進位顯示色票轉為 RGB 數值。colorVal
變化,並轉換為 PWM 數值發送。src\components\window-app-rgb-led-palette\window-app-rgb-led-palette.vue <script
// ...
export default {
name: 'WindowAppRgbLedPalette',
// ...
computed: {
// ...
/** 根據 colorVal 計算之 RGB 數值 */
rgbVal() {
if (this.colorVal === '') {
return {
r: 0,
g: 0,
b: 0,
};
}
return hexToRgb(this.colorVal);
},
},
watch: {
// ...
isSettingOk(isOk) {
if (!isOk) return;
this.initPins();
},
/** 若數值發生變化,發送命令 */
rgbVal(val) {
if (!this.isSettingOk) return;
this.writePinsVal(val);
},
},
// ...
methods: {
// ...
/** 初始化所有腳位 */
initPins() {
/** @type {PortTransceiver} */
const portTransceiver = this.portTransceiver;
Object.values(this.ledPin).forEach((/** @type {PinInfo} */ pin) => {
portTransceiver.addCmd('setMode', {
pin: pin.number,
mode: PWM,
});
});
},
/** 發送 PWM 數值
* @param {Object} rgbVal
* @param {number} rgbVal.r
* @param {number} rgbVal.g
* @param {number} rgbVal.b
*/
writePinsVal(rgbVal) {
/** @type {PortTransceiver} */
const portTransceiver = this.portTransceiver;
Object.entries(this.ledPin).forEach(
([key, /** @type {PinInfo} */ pin]) => {
const value = rgbVal[key];
portTransceiver.addCmd('setPwmValue', {
pin: pin.number,
value,
});
}
);
},
},
};
成功色彩數值轉為 PWM 發送!
可以發現一樣有塞車延遲的問題,所以一樣請出老朋友 throttle
src\components\window-app-rgb-led-palette\window-app-rgb-led-palette.vue <script
// ...
import { throttle } from 'lodash-es';
// ...
export default {
name: 'WindowAppRgbLedPalette',
// ...
data() {
return {
ledPin: {
r: null,
g: null,
b: null,
},
colorVal: '',
throttle: {
writePinsVal: null,
},
};
},
// ...
watch: {
// ...
rgbVal(val) {
if (!this.isSettingOk) return;
this.throttle.writePinsVal(val);
},
},
created() {
console.log(`[ ${this.$options.name} created ] id : `, this.id);
this.throttle.writePinsVal = throttle(this.writePinsVal, 100);
},
// ....
};
成功成為調色大師!(≧∀≦)
至此我們成功完成「RGB LED 調色盤」視窗,所以最後你想成為甚麼顏色呢?( ´ ▽ ` )ノ
如果 RGB LED 的混色效果不佳,可以拿砂紙將 LED 磨成霧面或是貼上霧面貼紙,會讓混色效果好一點。
進階挑戰:
細心的讀者們可能會注意到 RGB LED 的 3 種顏色發亮程度不同,這是因為不同 LED 的內阻、發光效率等等特性一定會有些許差異,導致同樣輸出下,亮度卻不一樣。
如何修正這個問題就留給各位讀者囉!(≖‿ゝ≖)✧
以上程式碼已同步至 GitLab,大家可以前往下載: