You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 00:01:17