整一个简单的LCD数字组件

96 阅读3分钟

说在前面

在项目实现看板功能的过程中有些地方需要用LCD字体来展示,差不多要长这样(随便截的图)

a1b6698bd7c4d8112f4aadd7b329f8d.png

我首先想到的方法是上网找个字体,但领导说用字体可能会吃版权炮,即便我找了可商用的字体甚至拿着License去找他也还是不同意,看来这个想法是到头了。

于是接下来我就写了几个svg大概模拟那个效果,用的时候要哪个数字就引入哪个数字呗,多简单。

通过svg实现效果

image.png

以8为例(因为8已经是最复杂的数字了),将数字看做如上图七个部分,画七个矩形。

<?xml version="1.0" encoding="UTF-8"?>
<svg width="12" height="21" viewBox="0 0 24 42" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <title>eight</title>
    <rect x="4" y="0" width="15" height="4"/>
    <rect x="0" y="4" width="4" height="15"/>
    <rect x="19" y="4" width="4" height="15"/>
    <rect x="4" y="19" width="15" height="4"/> 
    <rect x="0" y="23" width="4" height="15"/>
    <rect x="19" y="23" width="4" height="15"/>
    <rect x="4" y="38" width="15" height="4"/>
</svg>

大概是这个效果:

image.png (有点僵硬,在每个矩形加上圆角rx="2" ry="2",稍微顺眼一些)

image.png

差不多能使了,如果需要实现其他数字,只需要在8的基础上删除部分内容就可以。

封装成组件

之后在用的时候突然想到:既然数字被分为七个部分,如果我用一个包含七项的数组控制这七个部分的显示和隐藏,不就不需要整那么多svg文件引入了吗?比如我给他一个[1,1,1,1,1,1,1]告诉他七个部分都要显示,是否就能得到数字8了呢?假设这个数组就叫arr,项目使用vue

// 直接将svg写到页面中,
<template>
    <svg width="12" height="21" viewBox="0 0 24 42" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
        <rect v-if="arr[0] === 1" x="4" y="0" width="15" height="4"/>
        <rect v-if="arr[1] === 1" x="0" y="4" width="4" height="15"/>
        <rect v-if="arr[2] === 1" x="19" y="4" width="4" height="15"/>
        <rect v-if="arr[3] === 1" x="4" y="19" width="15" height="4"/> 
        <rect v-if="arr[4] === 1" x="0" y="23" width="4" height="15"/>
        <rect v-if="arr[5] === 1" x="19" y="23" width="4" height="15"/>
        <rect v-if="arr[6] === 1" x="4" y="38" width="15" height="4"/>
    </svg>
</template>

arr的值为[1,1,1,1,1,1,1],看看页面效果:

image.png

成了,那么只需要改动某一项的值就能展示其他数字了,例如我将值改为[0, 1, 1, 1, 0, 1, 0]

image.png

页面上的数字就变成了4。

那么剩下的就只有把数字转换成这种格式需要稍稍动一下脑子了。

首先我们需要给数组和数字建立一个对应关系,我在这里使用比较简单的方法:按照0-9的顺序将数组全部塞到一个数组hash中,正好对应的数字和索引也对的上:

        const hash = [
            [1, 1, 1, 0, 1, 1, 1], // 0
            [0, 0, 1, 0, 0, 1, 0], // 1
            [1, 0, 1, 1, 1, 0, 1], // 2
            [1, 0, 1, 1, 0, 1, 1], // 3
            [0, 1, 1, 1, 0, 1, 0], // 4
            [1, 1, 0, 1, 0, 1, 1], // 5
            [1, 1, 0, 1, 1, 1, 1], // 6
            [1, 0, 1, 0, 0, 1, 0], // 7
            [1, 1, 1, 1, 1, 1, 1], // 8
            [1, 1, 1, 1, 0, 1, 1], // 9
        ];

然后将接收到的数字转换为可遍历的字符串数组(或者直接字符串也行):

          const number = 123   // 假设传入的数字为123
          const StringArr = number.toString().split(''); // ["1","2","3"]
          // 然后遍历这个数组,将hash中对应的数组放到一个新数组中
          const data = []
          for (let i in StringArr) {
              data.push(hash[StringArr[i]]);
          }

我们便能拿到这样的结果:

image.png

那么遍历这个数组,就可以显示出当时传入的数字123了。

// 为了不显得太挤略微加了一点间隔
<template>
    <span v-for="item in data" style="margin-right:2px;">
        <svg width="12" height="21" viewBox="0 0 24 42" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
            <rect v-if="item[0] === 1" x="4" y="0" width="15" height="4"/>
            <rect v-if="item[1] === 1" x="0" y="4" width="4" height="15"/>
            <rect v-if="item[2] === 1" x="19" y="4" width="4" height="15"/>
            <rect v-if="item[3] === 1" x="4" y="19" width="15" height="4"/> 
            <rect v-if="item[4] === 1" x="0" y="23" width="4" height="15"/>
            <rect v-if="item[5] === 1" x="19" y="23" width="4" height="15"/>
            <rect v-if="item[6] === 1" x="4" y="38" width="15" height="4"/>
        </svg>
    </span>
</template>

好,康康效果:

image.png

嗯好,差不多有那个意思了。

稍作补充

因为当初做svg的时候没想太多,导致这个数字的宽高如果不按照一定比例给就会出现问题。。。但是既然要搞成组件,不能调整大小岂不显得很呆?

所以只接收一个宽度,高度用计算属性去算就好了,这样可以保证字的比例不会被改变。

// 为了不显得太挤略微加了一点间隔
 props: {
        size: {
            type: Number,
            default: 12,
        },
    },
 setup(props){
     // 这样写方便看
      const width = computed(() => {
         return props.size;
     });
     const height = computed(() => {
         return props.size / (4 / 7);   // 字体的宽高比为4:7
     });

 }

同理还可以加上颜色以及各个数字见的间隙等,完整代码:

template部分:

<template>
    <span v-for="(item, index) in data" :key="index" :style="`margin-right:${gutter}px`">
        <svg :width="width" :height="height" viewBox="0 0 24 42" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
            <rect v-if="item[0] === 1" :fill="numberColor" x="4" y="0" rx="2" ry="2" width="15" height="4"/>
            <rect v-if="item[1] === 1" :fill="numberColor" x="0" y="4" rx="2" ry="2" width="4" height="15"/>
            <rect v-if="item[2] === 1" :fill="numberColor" x="19" y="4" rx="2" ry="2" width="4" height="15"/>
            <rect v-if="item[3] === 1" :fill="numberColor" x="4" y="19" rx="2" ry="2" width="15" height="4"/>
            <rect v-if="item[4] === 1" :fill="numberColor" x="0" y="23" rx="2" ry="2" width="4" height="15"/>
            <rect v-if="item[5] === 1" :fill="numberColor" x="19" y="23" rx="2" ry="2" width="4" height="15"/>
            <rect v-if="item[6] === 1" :fill="numberColor" x="4" y="38" rx="2" ry="2" width="15" height="4"/>
        </svg>
    </span>
</template>

script部分:

<script lang="ts">
import { defineComponent, PropType, computed } from 'vue';
export default defineComponent({
    props: {
        size: {
            type: Number as PropType<number>,
            default: 12,
        },
        number: {
            type: Number as PropType<number>,
            default: 8,
        },
        color: {
            type: String as PropType<string>,
            default: '',
        },
        gutter: {
            type: Number as PropType<number>,
            default: 2,
        },
    },
    setup(props) {
        const hash = [
            [1, 1, 1, 0, 1, 1, 1],
            [0, 0, 1, 0, 0, 1, 0],
            [1, 0, 1, 1, 1, 0, 1],
            [1, 0, 1, 1, 0, 1, 1],
            [0, 1, 1, 1, 0, 1, 0],
            [1, 1, 0, 1, 0, 1, 1],
            [1, 1, 0, 1, 1, 1, 1],
            [1, 0, 1, 0, 0, 1, 0],
            [1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 0, 1, 1],
        ];
        const formatData = (StringArr) => {
            let resultArr: any = [];
            for (let i in StringArr) {
                resultArr.push(hash[StringArr[i]]);
            }
            console.log(resultArr, 'result');
            return resultArr;
        };
        const height = computed(() => {
            return props.size / (4 / 7);
        });
        const width = computed(() => {
            return props.size;
        });
        const numberColor = computed(() => {
            return props.color !== `` ? props.color : `#000000`;
        });
        const StringArr = computed(() => {
            return props.number.toString().split('');
        });

        const data = formatData(StringArr.value);

        return {
            data,
            formatData,
            height,
            width,
            numberColor,
            StringArr,
            hash,
        };
    },
});
</script>

这样一个简单的LCD数字组件就完成辣,虽然还有很大的优化空间,但已经可以交差了。

使用

<template>
    <div>
        <ledNumber :size="size" :number="data" :gutter="gutter" :color="color"></ledNumber>
    </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
import ledNumber from './ledNumberComponent.vue';
export default defineComponent({
    components: { ledNumber },
    setup() {
        const data = 2333333;
        const size = 30;
        const gutter = 6;
        const color = 'RGB(0,127,250)';
        return {
            data,
            size,
            gutter,
            color,
        };
    },
});
</script>

image.png