如何为HTML Canvas中可投掷的小球填充背景图片
用图片填充Canvas小球的解决方案
核心思路
使用Canvas的createPattern方法创建图片填充图案,需先确保图片加载完成,再将图案设为填充样式。同时要处理图片异步加载的逻辑,避免未加载完成就开始绘制。
修改后的完整代码
HTML部分
<canvas id="fetchCanvas"></canvas>
JavaScript部分
let canvas, ctx, vx = 0, vy = 0, radius = 30, cx = radius * 2, cy = radius * 2, gravity = 0.35, damping = 0.6, traction = 0.5, isHovering = false, throwActive = false, paused = false, ballPattern = null; // 存储图片填充图案 // 加载小球背景图片 const ballImage = new Image(); ballImage.src = "ball-background.svg"; ballImage.crossOrigin = "anonymous"; // 图片跨域时需设置 ballImage.onload = function() { // 创建重复图案,可改为repeat-x/repeat-y/no-repeat ballPattern = ctx.createPattern(ballImage, "repeat"); init(); // 图片加载完成后初始化Canvas }; function init() { canvas = document.getElementById("fetchCanvas"); ctx = canvas.getContext("2d"); resizeCanvas(); circle(); } // 单独处理Canvas尺寸调整,避免重复初始化 function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight - 20; } function circle() { ctx.clearRect(0, 0, canvas.width, canvas.height); if (!paused) { requestAnimationFrame(circle); // 碰撞检测与阻尼处理 if (cx + radius >= canvas.width) { vx = -vx * damping; cx = canvas.width - radius; } else if (cx - radius <= 0) { vx = -vx * damping; cx = radius; } if (cy + radius >= canvas.height) { vy = -vy * damping; cy = canvas.height - radius; vx *= traction; } else if (cy - radius <= 0) { vy = -vy * damping; cy = radius; } vy += gravity; cx += vx; cy += vy; } ctx.beginPath(); ctx.arc(cx, cy, radius, 0, 2 * Math.PI, false); // 用图片图案填充,未加载完成时用默认颜色兜底 ctx.fillStyle = ballPattern || "#FFDC5C"; ctx.fill(); } // 监听窗口resize事件调整Canvas尺寸 window.addEventListener('resize', resizeCanvas); // 拖拽投掷控制 canvas.addEventListener('mousedown', handleMouseDown); canvas.addEventListener('mouseup', handleMouseUp); canvas.addEventListener('mousemove', handleMouseMove); function handleMouseDown(e) { if (isHovering) { throwActive = true; dragStart = { x: e.pageX - canvas.offsetLeft, y: e.pageY - canvas.offsetTop, }; cx = e.pageX - canvas.offsetLeft; cy = e.pageY - canvas.offsetTop; vx = vy = 0; paused = true; } } function handleMouseMove(e) { const mouseX = parseInt(e.clientX - canvas.offsetLeft); const mouseY = parseInt(e.clientY - canvas.offsetTop); const dx = mouseX - cx; const dy = mouseY - cy; isHovering = (dx * dx + dy * dy < radius * radius); if (paused) { // 移除原init调用,避免重复初始化丢失图案 cx = e.pageX - canvas.offsetLeft; cy = e.pageY - canvas.offsetTop; // 手动触发一次绘制 circle(); } }; function handleMouseUp(e) { if (throwActive) { vx = (e.pageX - dragStart.x - canvas.offsetLeft) / 5; vy = (e.pageY - dragStart.y - canvas.offsetTop) / 5; paused = false; throwActive = false; circle(); } }
关键修改说明
- 图片加载逻辑:创建
Image对象并监听onload事件,确保图片加载完成后再创建填充图案,避免异步加载导致图案未就绪。 - 图案创建:通过
ctx.createPattern生成重复填充图案,可根据需求调整重复模式(如no-repeat需确保图片尺寸足够覆盖小球)。 - 修复resize问题:拆分原
init中的尺寸调整逻辑为独立函数,避免窗口resize时重复初始化Canvas导致图案丢失;同时移除拖拽时的init调用,防止重复初始化。 - 兜底处理:设置
fillStyle时添加默认颜色,确保图片未加载完成时小球仍有填充效果。
内容的提问来源于stack exchange,提问作者Cs238975
相关产品推荐
相关产品推荐

