1.问题描述
项目中使用canvas绘制一些线条,于是开始动手写了一些小demo。先在网页上创建了一个,然后通过CSS把它的width和height都设置为500px,然后我开始绘制一条从(0,0)到(250,250)的线,正常情况下应该展示为一条从canvas左上角到中心的斜线。
但是实际情况却大大出乎我的意料,这条线看起来经过了缩放,而且看起来宽和高的缩放比例不一样的。
<script setup lang="ts">
import { onMounted} from "vue";
onMounted(async () => {
const canvas = document.querySelector(`canvas`) as HTMLCanvasElement;
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
if (ctx) {
ctx.strokeStyle = `rgba(95, 189, 255, 1)`;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(250, 250);
ctx.stroke();
}
});
</script>
<template>
<div>
<canvas></canvas>
</div>
</template>
<style scoped>
canvas {
width: 500px;
height: 500px;
border: 1px solid black;
box-sizing: content-box;
}
</style>
2. 问题分析
分析线条的情况,可以推断出我指定的(250,250)这个位置,与canvas认为的不一致。canvas认为的(250,250)这个位置并不是在网页上500px X 500px 的中心。所以问题出现在canvas的真实大小与它展示的大小不一致。于是我查阅资料,打印了canvas的真实大小:
console.log(canvas.width);
console.log(canvas.height);
那现在问题就很明朗了,canvas的CSS像素为500px X 500px,这是它在网页上展示的大小,也称逻辑像素。而canvas它自己有个它认为的大小是300 × 150 ,所以我画(250,250)这个位置,就会超过canvas画布范围。
作为验证,可以绘制一条从(0,0)到(150,75)再回到(300,0)的线,看看是否到中心再回右上角:
<script setup lang="ts">
import { onMounted} from "vue";
onMounted(async () => {
const canvas = document.querySelector(`canvas`) as HTMLCanvasElement;
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
if (ctx) {
ctx.strokeStyle = `rgba(95, 189, 255, 1)`;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(150, 75);
ctx.lineTo(300, 0);
ctx.stroke();
}
});
</script>
<template>
<div>
<canvas></canvas>
</div>
</template>
<style scoped>
canvas {
width: 500px;
height: 500px;
border: 1px solid black;
box-sizing: content-box;
}
</style>
果然是这样:
3. 解决方法
3.1 设置canvas真实大小(在typescript/javascript代码中设置)
可以手动把canvas的真实大小设置为和CSS设置的大小500×500一样,这样就一致了。代码很简单:
const canvas = document.querySelector(`canvas`) as HTMLCanvasElement;
canvas.width = 500;
canvas.height = 500;
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
if (ctx) {
ctx.strokeStyle = `rgba(95, 189, 255, 1)`;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(250, 250);
ctx.lineTo(500, 0);
ctx.stroke();
}
这时候再绘制一条从(0,0)到(250,250)的线,就正常了:
3.2 设置canvas真实大小(在html5代码中内联设置)
<canvas width="500" height="500"></canvas>
效果是一样的,两种办法设置的是同一个东西。
console.log(canvas.width);
console.log(canvas.height);
3.3 在 < canvas >中更正分辨率(官方推荐的方法!!!强烈建议!!!)
官方文档里推荐了一种更正分辨率的方法,实测这种办法充分考虑了物理像素和逻辑像素的差异,并进行修正,效果更好,更清晰!!!
const canvas = document.querySelector(`canvas`) as HTMLCanvasElement;
// Set display size (css pixels).
const size = 500;
canvas.style.width = size + "px";
canvas.style.height = size + "px";
// Set actual size in memory (scaled to account for extra pixel density).
const scale = window.devicePixelRatio; // Change to 1 on retina screens to see blurry canvas.
canvas.width = Math.floor(size * scale);
canvas.height = Math.floor(size * scale);
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
// Normalize coordinate system to use css pixels.
ctx.scale(scale, scale);
if (ctx) {
ctx.strokeStyle = `rgba(95, 189, 255, 1)`;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(250, 250);
ctx.lineTo(500, 0);
ctx.stroke();
}