深入理解Canvas分辨率概念,高DPI屏幕适配方案,包含多个交互式案例 Canvas的分辨率问题在高DPI屏幕(如Retina屏)上尤为明显。理解分辨率概念对于创建清晰的图形至关重要。
分辨率指的是图像的精细程度:
设备像素比(Device Pixel Ratio)表示一个CSS像素对应多少个物理像素:
const dpr = window.devicePixelRatio || 1
// 普通屏幕: 1
// Retina屏幕: 2
// 高端手机: 3
普通绑制(未适配高DPI)
高DPI适配
<canvas id="normalCanvas" width="200" height="100" style="width: 200px; height: 100px;"></canvas>
<canvas id="hiDPICanvas" style="width: 200px; height: 100px;"></canvas>
<script>
const dpr = window.devicePixelRatio || 1
// 普通绑制(未适配高DPI)
const normalCanvas = document.getElementById('normalCanvas')
const normalCtx = normalCanvas.getContext('2d')
normalCtx.strokeStyle = '#3498db'
normalCtx.lineWidth = 1
normalCtx.beginPath()
normalCtx.arc(50, 50, 30, 0, Math.PI * 2)
normalCtx.stroke()
// 高DPI适配
const hiDPICanvas = document.getElementById('hiDPICanvas')
const hiDPICtx = hiDPICanvas.getContext('2d')
// 设置画布尺寸为显示尺寸的dpr倍
hiDPICanvas.width = 200 * dpr
hiDPICanvas.height = 100 * dpr
// 缩放上下文以匹配CSS尺寸
hiDPICtx.scale(dpr, dpr)
// 正常绑制(使用CSS尺寸)
hiDPICtx.strokeStyle = '#3498db'
hiDPICtx.lineWidth = 1
hiDPICtx.beginPath()
hiDPICtx.arc(50, 50, 30, 0, Math.PI * 2)
hiDPICtx.stroke()
</script>
| 步骤 | 说明 |
|---|---|
canvas.width = w * dpr | 画布缓冲区放大dpr倍 |
canvas.style.width = w + 'px' | CSS显示尺寸保持不变 |
ctx.scale(dpr, dpr) | 缩放绑制上下文 |
<canvas id="myCanvas" style="width: 400px; height: 200px;"></canvas>
<script>
function setupHiDPICanvas(canvas) {
const dpr = window.devicePixelRatio || 1
const rect = canvas.getBoundingClientRect()
// 设置画布缓冲区尺寸
canvas.width = rect.width * dpr
canvas.height = rect.height * dpr
const ctx = canvas.getContext('2d')
// 缩放上下文
ctx.scale(dpr, dpr)
// 返回CSS尺寸供绑制使用
return {
width: rect.width,
height: rect.height,
dpr: dpr,
ctx: ctx
}
}
// 使用
const canvas = document.getElementById('myCanvas')
const { width, height, ctx } = setupHiDPICanvas(canvas)
// 正常绑制(使用CSS尺寸)
ctx.strokeStyle = '#3498db'
ctx.lineWidth = 2
ctx.beginPath()
ctx.arc(width / 2, height / 2, 60, 0, Math.PI * 2)
ctx.stroke()
</script>
| 函数返回值 | 说明 |
|---|---|
width | CSS显示宽度 |
height | CSS显示高度 |
dpr | 设备像素比 |
ctx | 已缩放的绑制上下文 |
<div id="container" style="width: 100%;">
<canvas id="myCanvas" style="width: 100%;"></canvas>
</div>
<script>
const canvas = document.getElementById('myCanvas')
const container = document.getElementById('container')
const dpr = window.devicePixelRatio || 1
function setupCanvas() {
const w = container.clientWidth
const h = Math.round(w * 0.5)
// 设置CSS尺寸
canvas.style.width = w + 'px'
canvas.style.height = h + 'px'
// 设置缓冲区尺寸(高DPI)
canvas.width = w * dpr
canvas.height = h * dpr
const ctx = canvas.getContext('2d')
// 重置变换并应用DPR缩放
ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
return { w, h, ctx }
}
function draw() {
const { w, h, ctx } = setupCanvas()
// 绑制内容
ctx.fillStyle = '#3498db'
ctx.fillRect(w / 2 - 50, h / 2 - 25, 100, 50)
}
// 初始化
draw()
// 响应窗口大小变化
window.addEventListener('resize', draw)
</script>
| 方法 | 说明 |
|---|---|
setTransform(dpr, 0, 0, dpr, 0, 0) | 直接设置变换矩阵 |
cancelAnimationFrame() | 取消之前的动画帧 |
| resize时重绘 | 窗口变化时重新设置画布 |
<script>
function getScreenInfo() {
const dpr = window.devicePixelRatio || 1
const screenWidth = window.screen.width
const screenHeight = window.screen.height
const physicalWidth = Math.round(screenWidth * dpr)
const physicalHeight = Math.round(screenHeight * dpr)
let screenType = '普通屏幕'
if (dpr >= 3) screenType = '超高DPI'
else if (dpr >= 2) screenType = 'Retina/高DPI'
else if (dpr >= 1.5) screenType = '中等DPI'
return {
dpr,
screenWidth,
screenHeight,
physicalWidth,
physicalHeight,
screenType
}
}
const info = getScreenInfo()
console.log('设备像素比:', info.dpr)
console.log('屏幕类型:', info.screenType)
console.log('CSS像素:', info.screenWidth, 'x', info.screenHeight)
console.log('物理像素:', info.physicalWidth, 'x', info.physicalHeight)
</script>
| 属性 | 说明 |
|---|---|
window.devicePixelRatio | 设备像素比 |
window.screen.width | 屏幕CSS像素宽度 |
screenWidth * dpr | 物理像素宽度 |
const CanvasUtils = {
setupHiDPI(canvas) {
const dpr = window.devicePixelRatio || 1
const rect = canvas.getBoundingClientRect()
canvas.width = rect.width * dpr
canvas.height = rect.height * dpr
const ctx = canvas.getContext('2d')
ctx.scale(dpr, dpr)
return {
width: rect.width,
height: rect.height,
dpr,
ctx
}
},
clearCanvas(canvas) {
const ctx = canvas.getContext('2d')
const dpr = window.devicePixelRatio || 1
ctx.setTransform(1, 0, 0, 1, 0, 0)
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.scale(dpr, dpr)
},
getCanvasSize(canvas) {
const rect = canvas.getBoundingClientRect()
return {
width: rect.width,
height: rect.height
}
}
}
Canvas分辨率要点:
ctx.scale(dpr, dpr)简化绑制window.devicePixelRatio