说在前面
在项目实现看板功能的过程中有些地方需要用LCD字体来展示,差不多要长这样(随便截的图)
我首先想到的方法是上网找个字体,但领导说用字体可能会吃版权炮,即便我找了可商用的字体甚至拿着License去找他也还是不同意,看来这个想法是到头了。
于是接下来我就写了几个svg大概模拟那个效果,用的时候要哪个数字就引入哪个数字呗,多简单。
通过svg实现效果
以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>
大概是这个效果:
(有点僵硬,在每个矩形加上圆角
rx="2" ry="2"
,稍微顺眼一些)
差不多能使了,如果需要实现其他数字,只需要在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]
,看看页面效果:
成了,那么只需要改动某一项的值就能展示其他数字了,例如我将值改为[0, 1, 1, 1, 0, 1, 0]
页面上的数字就变成了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]]);
}
我们便能拿到这样的结果:
那么遍历这个数组,就可以显示出当时传入的数字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>
好,康康效果:
嗯好,差不多有那个意思了。
稍作补充
因为当初做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>