D20 - 「呐,你想要成为什麽颜色?」:将色彩传下去

这个章节来实作彩球组件,让使用者可以选择颜色。

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

建立颜色控制器

建立 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')
		// ...

D20 - 完成彩球基本样式.gif

球球出现了!

接下来依序完成 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')

实测功能。

D20 - 彩球选择颜色功能.gif

成功变色!最後让我们加入色环。

实作以下功能:

  • 使用 Quasar Knob 建立色环。
  • 将 16 进位表示色票转为 R、G、B 数值

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。

看看效果如何。

D20 - 色环效果.gif

感觉真不错!(ง •̀_•́)ง

最後就是实际让 RGB LED 发亮了!让我们回到 window-app-rgb-led-palette.vue

功能需求为:

  • 脚位设定完成後,初始化所有脚位为 PWM 模式。
  • 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 发送!

D20 - 颜色输出至 RGB LED.gif

可以发现一样有塞车延迟的问题,所以一样请出老朋友 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);
  },
  // ....
};

D20 - PWM 输出加入 throttle.gif

成功成为调色大师!(≧∀≦)

至此我们成功完成「RGB LED 调色盘」视窗,所以最後你想成为甚麽颜色呢?( ´ ▽ ` )ノ

如果 RGB LED 的混色效果不佳,可以拿砂纸将 LED 磨成雾面或是贴上雾面贴纸,会让混色效果好一点。

进阶挑战:

细心的读者们可能会注意到 RGB LED 的 3 种颜色发亮程度不同,这是因为不同 LED 的内阻、发光效率等等特性一定会有些许差异,导致同样输出下,亮度却不一样。

如何修正这个问题就留给各位读者罗!(≖‿ゝ≖)✧

总结

  • 将色彩数值透过 RGB LED 与 PWM 调光呈现。
  • 完成「RGB LED 调色盘」视窗。

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

GitLab - D20


<<:  DAY 22 『 连接 API 实作 - 天气 APP 』Part4

>>:  day19 Kotlin coroutine flow with liveData in MVVM

DAY 12『 利用安装套件管理工具 ( CocoaPods ) 下载资料库( Realm Studio ) 』

打开 terminal ( command + space )输入以下指令安装 CocoaPods...

Day26:管理 LXC 的好工具 —— Docker

前言 明天我将会介绍如何在 VM 上用 Docker 建立 Tensorflow 开发环境。 为了明...

【Day 10】Google Apps Script - 环境篇回顾整理

停留回想:要进入下一篇前,整理回顾一下环境篇的笔记思绪。 今日要点: 》环境篇回顾整理 》系列目录...

14.移转 Aras PLM大小事-开发报表 Where Used Top Level Product

本篇放上获得最上阶产品料号的作法 1.建立报表 2.设定SQL语法 3.设定Method 4.设定X...

DAY12 - [JS] 延伸ToDoList - 完成、未完成分类

今日文章目录 需求说明 事前准备 参考资料 需求说明 Q: 有哪些需求? 纪录分类:全部、已完成、...