在vue中可以通过「v-*」来定义html标签的属性,但是canvas属性不能直接接收。作者本以为这是一个很简单的问题,但是最后却饶了很多路才解决,并放出自己的解决方案。
作者使用Vue中的directive(指令)来解决canvas元素与外界ViewModel保持数据同步的操作。自定义指令可以让我们为自己的template(模板)使用v-something,并定义自己的行为。
一下是以单文件类组件为例的一段示例代码,且使用TypeScript语法。
<template>
<div class="rxcanvas">
<span>{{ size }}</span>
<input type="range" min="1" max="100" step="5" id="size" v-model="size">
<label for="size">- Size</label>
<p><canvas></canvas></p>
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import Dot from "@/dot"; // defined below
@Component
export default class RxCanvas extends Vue {
private data() {
return {
size: 10
};
}
// computed property
get dot(): Dot {
return new Dot(this.$data.size);
}
}
</script>
<style scoped>
</style>
Dot类可以将自己将canvas元素会知道一个对象上。
// dot.ts
export default class Dot {
private readonly color: string = "#000";
constructor(private radius: number) { }
public draw(canvas: HTMLCanvasElement): void {
// resize canvas to dot size
const canvasDim = this.radius * 2;
canvas.width = canvasDim;
canvas.height = canvasDim;
// get context for drawing
const ctx = canvas.getContext('2d')!;
// start with a blank slate
ctx.clearRect(0, 0, canvas.width, canvas.height);
// find the centerpoint
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
// create the shape
ctx.beginPath();
ctx.arc(centerX, centerY, this.radius, 0, 2 * Math.PI, false);
ctx.fillStyle = this.color;
ctx.fill();
ctx.stroke();
}
}
在这之后,将这个自定义指令命名为 draw(你可以命名成任意的名字),我们将dot传入,dot在RxCanvas类中定义的计算属性,只要size变化,这个计算属性会创建一个有正确size的新的Dot。
自定义指令在Vue组件内定义,如果使用了vue-property-decorator,可以使用以下方式定义:
@Component({
directives: {
"draw": function(canvasElement, binding) {
// casting because custom directives accept an `Element` as the first parameter
binding.value.draw(canvasElement as HTMLCanvasElement);
}
}
})
export default class RxCanvas extends Vue {
// data(), dot(), etc
}
要想让指令在属性发生变化时,重新绘制,代码需要改成如下这样。
directives: {
draw: {
bind: function(canvasElement: Element, binding: VNodeDirective) {
binding.value.draw(canvasElement as HTMLCanvasElement);
},
update: function(canvasElement, binding) {
binding.value.draw(canvasElement as HTMLCanvasElement);
}
}
}
bind函数只会在组件被创建的时候触发,update函数会在RxCanvas类创建的VNode示例发生变化时触发,包括data的变化。
如果想要在项目全局内使用这个自定义指令,需要将它定义在全局下,如下:
// index.ts
Vue.directive('draw': function(canvasElement, binding) {
binding.value.draw(canvasElement as HTMLCanvasElement);
});
希望这篇文章能给你启发,让你更好地运用Vue的自定义指令来创建更加丰富的可复用组件。