D19 - 「呐,你想要成为什麽颜色?」:打造一个调色盘吧!

接下来我们要利用「 PWM」与「RGB LED」设计调色盘功能。

首先来认识一下新朋友 - 「RGB LED」

RGB LED

D19 - RGB LED.png

电子助教:「所以为甚麽要把三种 LED 合在一起?( ´ ▽ ` )ノ」

鳕鱼:「这就是一个很长的故事了。很久以前,R LED 与 G LED 的祖先们发生了不可告人的...」

电子助教:「不要瞎掰好吗,明明就是为了要混色!(°c_,°`)」

鳕鱼:「你明明就知道是问个毛线喔... ('◉◞⊖◟◉` )」

D19 - RGB 混合.png

硬体实作

需要准备以下设备与零件:

  • 三用电表 * 1

  • 面包板 * 1

  • RGB LED * 1

    推荐大家买这种模组式的 RGB LED,因为上面已经配好电阻,可以直接接线。

    2021-09-17 23.12.17.jpg

检查硬体

老样子要检查小夥伴们是否正常。

RGB LED

测试方式与一般 LED 概念相同。

D19 - 确认 RGB LED 正常.png

若有任一种颜色没有亮,表示此颜色之 LED 损坏,请换一个 RGB LED。

连接电路

以下为参考接线方式,可以不用完全相同,只要效果相同即可。

使用 Uno 板子上的 5V 为 +、GND 为 -。

D19 - RGB LED 电路.png

建立视窗

window-example.vue 复制一份後改个名字,建立 window-app-rgb-led-palette.vue

调色盘相关档案建立一个 src\components\window-app-rgb-led-palette\ 资料夹整理。

src\components\window-app-rgb-led-palette\window-app-rgb-led-palette.vue

<template lang="pug">
base-window.window-app-rgb-led-palette(
  :pos='pos',
  header-icon-color='blue-grey',
  header-icon='r_palette',
  body-class='c-col h-330px',
  title='RGB LED 调色盘'
)
</template>

<style lang="sass">
</style>

<script>
import mixinWindow from '@/mixins/mixin-window';

import BaseWindow from '@/components/base-window.vue';

export default {
  name: 'WindowAppRgbLedPalette',
  components: {
    'base-window': BaseWindow,
  },
  mixins: [mixinWindow],
  props: {},
  data() {
    return {};
  },
  computed: {},
  watch: {},
  created() {
    console.log(`[ ${this.$options.name} created ] id : `, this.id);
  },
  mounted() {},
  methods: {},
};
</script>

接着回到 app.vue,将右键选单内加入『新增「RGB LED 调色盘」』选项,并引入组件。

src\app.vue <template lang="pug">

.screen(@click='handleClick')
  // ...

	// 右键选单
  q-menu(context-menu, content-class='border-radius-s')
    q-list.min-w-260px
      q-item(@click='addWindow("window-digital-io")', clickable, v-close-popup)
        q-item-section
          | 新增「数位 I/O 视窗」
			q-item(@click='addWindow("window-analog-input")', clickable, v-close-popup)
        q-item-section
          | 新增「类比输入视窗」
			q-item(@click='addWindow("window-pwm-output")', clickable, v-close-popup)
        q-item-section
          | 新增「PWM 输出视窗」
			q-separator

      q-item(
        @click='addWindow("window-app-rgb-led-palette")',
        clickable,
        v-close-popup
      )
        q-item-section
          | 新增「RGB LED 调色盘」

src\app.vue <script>

// ...

import WindowDigitalIo from '@/components/window-digital-io.vue';
import WindowAnalogInput from '@/components/window-analog-input.vue';
import WindowPwmOutput from '@/components/window-pwm-output.vue';

import WindowAppRgbLedPalette from '@/components/window-app-rgb-led-palette/window-app-rgb-led-palette.vue';

export default {
  name: 'App',
  components: {
		'dialog-system-setting': DialogSystemSetting,
    
    'window-digital-io': WindowDigitalIo,
    'window-analog-input': WindowAnalogInput,
    'window-pwm-output': WindowPwmOutput,

		'window-app-rgb-led-palette': WindowAppRgbLedPalette,
  },
  // ...
};

规划 UI

D19 - window-app-rgb-led-palette 线框 (1).png

实作设定栏位

增加以下程序:

  • 引入 base-select-pin.vue
  • data()
    • ledPin 储存 RGB 3 个脚位。
    • colorVal 储存目前颜色。
  • methods
    • handleErr(msg) 处理错误讯息

src\components\window-app-rgb-led-palette\window-app-rgb-led-palette.vue <script>

import mixinWindow from '@/mixins/mixin-window';

import BaseWindow from '@/components/base-window.vue';
import BaseSelectPin from '@/components/base-select-pin.vue';

export default {
  name: 'WindowAppRgbLedPalette',
  components: {
    'base-window': BaseWindow,
    'base-select-pin': BaseSelectPin,
  },
  mixins: [mixinWindow],
  props: {},
  data() {
    return {
      ledPin: {
        r: null,
        g: null,
        b: null,
      },

      colorVal: '',
    };
  },
  computed: {},
  watch: {},
  created() {
    console.log(`[ ${this.$options.name} created ] id : `, this.id);
  },
  mounted() {},
  methods: {
		handleErr(msg) {
      this.$q.notify({
        type: 'negative',
        message: msg,
      });
    },
	},
};

src\components\window-app-rgb-led-palette\window-app-rgb-led-palette.vue <template lang="pug">

base-window.window-app-rgb-led-palette(
  :pos='pos',
  header-icon-color='blue-grey',
  header-icon='r_palette',
  body-class='c-col h-330px',
  title='RGB LED 调色盘'
)
  .h-full.overflow-hidden
    .setting-form
      .text-14px.mb-14px
        | 设定 RGB LED 脚位
      .led-item
        q-icon(name='r_emoji_objects', color='red-5', size='34px')
        base-select-pin.w-full(
          v-model='ledPin.r',
          color='red-2',
          placeholder='点击选择',
          @err='handleErr'
        )

      .led-item
        q-icon(name='r_emoji_objects', color='green-5', size='34px')
        base-select-pin.w-full(
          v-model='ledPin.g',
          color='green-2',
          placeholder='点击选择',
          @err='handleErr'
        )

      .led-item
        q-icon(name='r_emoji_objects', color='blue-5', size='34px')
        base-select-pin.w-full(
          v-model='ledPin.b',
          color='blue-2',
          placeholder='点击选择',
          @err='handleErr'
        )

src\components\window-app-rgb-led-palette\window-app-rgb-led-palette.vue <style lang="sass">

@import '@/styles/quasar.variables.sass'

.window-app-rgb-led-palette
  width: 300px
  .setting-form
    position: absolute
    padding: 30px
    width: 100%
    height: 100%
    .led-item
      display: flex
      align-items: center
      margin-bottom: 10px
      .q-icon
        margin-right: 14px

设定栏位外观完成。

Untitled

功能需求为:

  • 提供所有支援 PWM 功能脚位清单。
  • 判断是否所有脚位都选择完成。
  • 回报 Vuex 新增、移除占用脚位。

src\components\window-app-rgb-led-palette\window-app-rgb-led-palette.vue <script>

/**
 * @typedef {import('@/script/modules/port-transceiver').default} PortTransceiver
 *
 * @typedef {import('@/types/type').PinInfo} PinInfo
 * @typedef {import('@/types/type').PinCapability} PinCapability
 */

import { mapState } from 'vuex';

// ...

import { PinMode } from '@/script/utils/firmata.utils';
const { PWM } = PinMode;

export default {
  name: 'WindowAppRgbLedPalette',
	// ...
	watch: {
    'ledPin.r': {
      handler(newVal, oldVal) {
        this.handlePinSelect(newVal, oldVal);
      },
    },
    'ledPin.g': {
      handler(newVal, oldVal) {
        this.handlePinSelect(newVal, oldVal);
      },
    },
    'ledPin.b': {
      handler(newVal, oldVal) {
        this.handlePinSelect(newVal, oldVal);
      },
    },
  },
  // ...
  computed: {
    ...mapState({
      boardPins: (state) => state.board.info.pins,

      /** @type {PortTransceiver} */
      portTransceiver: (state) => state.core.transceiver,
    }),

		/** 支援功能的脚位 */
    supportPins() {
      /** @type {PinInfo[]} */
      const boardPins = this.boardPins;

      return boardPins.filter((pin) =>
        pin.capabilities.some((capability) => PWM === capability.mode)
      );
    },

    /** 判断所有 Pin 脚是否选择完成 */
    isSettingOk() {
      return Object.values(this.ledPin).every((pin) => pin);
    },
  },
  // ...
	methods: {
    // ...

    /** 处理脚位选择事件,回报 Vuex 新增、移除占用脚位
     * @param {PinInfo} newVal
     * @param {PinInfo} oldVal
     */
    handlePinSelect(newVal, oldVal) {
      if (newVal) {
        this.$store.commit('window/addOccupiedPin', {
          id: this.id,
          pin: newVal,
        });
      }

      if (oldVal) {
        this.$store.commit('window/deleteOccupiedPin', {
          id: this.id,
          pin: oldVal,
        });
      }
    },
  },
};

将每个 base-select-pin 传入 supportPins

src\components\window-app-rgb-led-palette\window-app-rgb-led-palette.vue <template lang="pug">

base-window.window-app-rgb-led-palette(
  :pos='pos',
  header-icon-color='blue-grey',
  header-icon='r_palette',
  body-class='c-col h-330px',
  title='RGB LED 调色盘'
)
  .h-full.overflow-hidden
    .setting-form
      .text-14px.mb-14px
        | 设定 RGB LED 脚位
      .led-item
        q-icon(name='r_emoji_objects', color='red-5', size='34px')
        base-select-pin.w-full(
          v-model='ledPin.r',
          :pins='supportPins',
          color='red-2',
          placeholder='点击选择',
          @err='handleErr'
        )

      .led-item
        q-icon(name='r_emoji_objects', color='green-5', size='34px')
        base-select-pin.w-full(
          v-model='ledPin.g',
          :pins='supportPins',
          color='green-2',
          placeholder='点击选择',
          @err='handleErr'
        )

      .led-item
        q-icon(name='r_emoji_objects', color='blue-5', size='34px')
        base-select-pin.w-full(
          v-model='ledPin.b',
          :pins='supportPins',
          color='blue-2',
          placeholder='点击选择',
          @err='handleErr'
        )

尝试看看。

D19 - 判断所有脚位是否选择完成.gif

成功!再来就是自动切换画面的部分,我们使用 transition 达成效果。

src\components\window-app-rgb-led-palette\window-app-rgb-led-palette.vue <template lang="pug">

base-window.window-app-rgb-led-palette(
  :pos='pos',
  header-icon-color='blue-grey',
  header-icon='r_palette',
  body-class='c-col h-330px',
  title='RGB LED 调色盘'
)
  .h-full.overflow-hidden
		// 设定完成後,显示这一区
    transition(name='switch-right')
      .text-30px.absolute(v-if='isSettingOk') (´,,•ω•,,)

		// 设定完成前,显示设定栏位
    transition(name='switch-left')
      .setting-form(v-if='!isSettingOk')
        .text-14px.mb-14px
          | 设定 RGB LED 脚位
        .led-item
          q-icon(name='r_emoji_objects', color='red-5', size='34px')
          base-select-pin.w-full(
            v-model='ledPin.r',
            :pins='supportPins',
            color='red-2',
            placeholder='点击选择',
            @err='handleErr'
          )

        .led-item
          q-icon(name='r_emoji_objects', color='green-5', size='34px')
          base-select-pin.w-full(
            v-model='ledPin.g',
            :pins='supportPins',
            color='green-2',
            placeholder='点击选择',
            @err='handleErr'
          )

        .led-item
          q-icon(name='r_emoji_objects', color='blue-5', size='34px')
          base-select-pin.w-full(
            v-model='ledPin.b',
            :pins='supportPins',
            color='blue-2',
            placeholder='点击选择',
            @err='handleErr'
          )

定义过场动画。

src\components\window-app-rgb-led-palette\window-app-rgb-led-palette.vue <style lang="sass">

@import '@/styles/quasar.variables.sass'

.window-app-rgb-led-palette
  width: 300px
  // ...

  .switch-left-enter-active, .switch-left-leave-active
    transition-timing-function: cubic-bezier(0.945, -0.290, 0.165, 1.360)
    transition-duration: 0.8s
    pointer-events: none
  .switch-left-enter, .switch-left-leave-to
    transform: translateX(100%) !important
    opacity: 0 !important

  .switch-right-enter-active, .switch-right-leave-active
    transition-timing-function: cubic-bezier(0.945, -0.290, 0.165, 1.360)
    transition-duration: 0.8s
    pointer-events: none
  .switch-right-enter, .switch-right-leave-to
    transform: translateX(-100%) !important
    opacity: 0 !important

实测效果。

D19 - 脚位设定完成後自动切换画面.gif

成功切换画面了!✧*。٩(ˊᗜˋ*)و✧*。

下一章节进入实作彩球的部分!

总结

  • 认识 RGB LED。
  • 成功建立「RGB LED 调色盘」视窗。

以上程序码已同步至 GitLab,大家可以前往下载:

GitLab - D19


<<:  赋权-团队里,没有人是局外人

>>:  Day18 iPhone捷径-这是在哪里拍摄的

Day 26 - "不在办公室里"工作已快成为常态

图片来源 这标题虽然有点耸动, 但也是"软件资讯业"的业态发展趋势, 虽然新闻...

追求JS小姊姊系列 Day25 -- 工具人、姐妹的存活原理:宣告变数的有效区域

前情提要: 看完记忆体储存差异,现在要来谈谈全域污染这件事。 基本scope概念 所谓的范畴Scop...

[Day21] still placeholder

写在前面 still placeholder still placeholder still pla...

Mongoose Schema TimeZone

关於 时区 因为取资料的方式都以时间线的方式取得(http, websocket) 所以时区是一个必...

Day40 ( 游戏设计 ) 反弹球 ( 乒乓球 )

反弹球 ( 乒乓球 ) 教学原文参考:反弹球 ( 乒乓球 ) 这篇文章会介绍,如何在 Scratch...