37 lines
2.5 KiB
Vue
37 lines
2.5 KiB
Vue
<script setup lang="ts">
|
||
import { computed,getCurrentInstance,nextTick,onMounted,watch } from 'vue'
|
||
import { cellAtPoint } from '../core/canvas'
|
||
import type { Pattern } from '../core/types'
|
||
let props=withDefaults(defineProps<{pattern:Pattern;size?:number;highlight?:string;grid?:boolean;completed?:number[]}>(),{size:160,highlight:'',grid:false,completed:()=>[]})
|
||
let emit=defineEmits<{cell:[index:number]}>()
|
||
let instance=getCurrentInstance(); let id='p'+instance?.uid
|
||
let height=computed(()=>Math.round(props.size*props.pattern.height/props.pattern.width))
|
||
async function draw(){await nextTick(); let p=props.pattern;let ctx=uni.createCanvasContext(id,instance);let s=props.size/p.width
|
||
ctx.clearRect(0,0,props.size,height.value);ctx.setFillStyle('#f0efe5');ctx.fillRect(0,0,props.size,height.value)
|
||
let palette=new Map(p.colors.map(c=>[c.code,c.hex]));let done=new Set(props.completed)
|
||
p.cells.forEach((c,i)=>{if(!c)return;ctx.setGlobalAlpha(props.highlight&&c!==props.highlight? .15:1);ctx.setFillStyle(palette.get(c)||'#777');let x=i%p.width*s,y=Math.floor(i/p.width)*s;ctx.fillRect(x,y,s,s);ctx.setGlobalAlpha(1)
|
||
if(props.grid&&s>=24){ctx.setFillStyle('#fff');ctx.fillRect(x+1,y+1,s-2,11);ctx.setFillStyle('#253d32');ctx.setFontSize(8);ctx.fillText(c,x+2,y+9)}
|
||
if(done.has(i)){ctx.setFillStyle('rgba(255,255,255,0.67)');ctx.fillRect(x,y,s,s);ctx.setFillStyle('#265b49');ctx.setFontSize(Math.max(9,s*.6));ctx.fillText('✓',x+s*.15,y+s*.8)}
|
||
});
|
||
if(props.grid){
|
||
ctx.setStrokeStyle('rgba(100,119,98,0.24)');ctx.setLineWidth(.5);ctx.beginPath()
|
||
for(let column=0;column<=p.width;column++){ctx.moveTo(column*s,0);ctx.lineTo(column*s,height.value)}
|
||
for(let row=0;row<=p.height;row++){ctx.moveTo(0,row*s);ctx.lineTo(props.size,row*s)}
|
||
ctx.stroke()
|
||
}
|
||
ctx.draw()
|
||
}
|
||
function tap(e:any){
|
||
let touch=e.changedTouches?.[0]||e.touches?.[0]
|
||
let x=touch?.clientX??e.detail?.x,y=touch?.clientY??e.detail?.y
|
||
// 事件坐标属于视口,必须扣除画布在滚动容器中的实际偏移。
|
||
uni.createSelectorQuery().in(instance?.proxy).select('#'+id).boundingClientRect((value:any)=>{
|
||
if(!value)return
|
||
let cell=cellAtPoint(x,y,value,props.pattern.width,props.pattern.height)
|
||
if(cell!==null)emit('cell',cell)
|
||
}).exec()
|
||
}
|
||
onMounted(draw);watch(()=>[props.pattern,props.size,props.highlight,props.completed],draw,{deep:true})
|
||
</script>
|
||
<template><canvas :id="id" :canvas-id="id" :style="{width:size+'px',height:height+'px'}" :aria-label="pattern.title+','+pattern.width+'乘'+pattern.height+'格'" @tap="tap" /></template>
|