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

如何用JavaScript实现圆形的平面阴影效果(基于p5.js)

实现圆形的平面投射阴影(p5.js)

你已经完成了切线计算的核心部分,只需要将切线向外延伸到屏幕边界,再构建出阴影的封闭区域即可。以下是修改后的完整方案:

关键调整点

  • 修复代码语法错误:noStroke; → noStroke();
  • 计算切线延伸方向,将切点处的切线向外延伸至屏幕边缘,得到阴影的两个端点
  • 重新构建阴影路径:连接两个切点和对应的延伸端点,形成完整的阴影区域

修改后的完整代码

function setup() {
  createCanvas(windowWidth, windowHeight);
}

function draw() {
  background("#9B009D");

  const circleCenterX = width / 2;
  const circleCenterY = height / 2;
  const circleRadius = 200;

  // 先绘制圆形,确保阴影在圆形下方
  fill("#FF7000");
  noStroke();
  ellipse(circleCenterX, circleCenterY, circleRadius * 2);

  // 鼠标作为光源绘制阴影和切线
  if (mouseX !== 0 || mouseY !== 0) {
    drawShadow(mouseX, mouseY, circleCenterX, circleCenterY, circleRadius);
  }
}

function drawShadow(lightX, lightY, centerX, centerY, radius) {
  const dx = lightX - centerX;
  const dy = lightY - centerY;
  const directDistance = sqrt(dx * dx + dy * dy);

  // 光源在圆内时不绘制阴影
  if (directDistance < radius) {
    return;
  }

  // 计算切线相关角度
  const angleToCenter = atan2(dy, dx);
  const angleFromTangent = acos(radius / directDistance);
  const angle1 = angleToCenter + angleFromTangent;
  const angle2 = angleToCenter - angleFromTangent;

  // 计算圆上的两个切点
  const tanPoint1X = centerX + radius * cos(angle1);
  const tanPoint1Y = centerY + radius * sin(angle1);
  const tanPoint2X = centerX + radius * cos(angle2);
  const tanPoint2Y = centerY + radius * sin(angle2);

  // 计算切线延伸方向(从光源指向切点,向外延伸)
  const dir1X = tanPoint1X - lightX;
  const dir1Y = tanPoint1Y - lightY;
  const dir2X = tanPoint2X - lightX;
  const dir2Y = tanPoint2Y - lightY;

  // 延伸切线到屏幕外(取屏幕对角线2倍长度确保超出边界)
  const extendLength = sqrt(width*width + height*height) * 2;
  const shadowEnd1X = tanPoint1X + dir1X / sqrt(dir1X*dir1X + dir1Y*dir1Y) * extendLength;
  const shadowEnd1Y = tanPoint1Y + dir1Y / sqrt(dir1X*dir1X + dir1Y*dir1Y) * extendLength;
  const shadowEnd2X = tanPoint2X + dir2X / sqrt(dir2X*dir2X + dir2Y*dir2Y) * extendLength;
  const shadowEnd2Y = tanPoint2Y + dir2Y / sqrt(dir2X*dir2X + dir2Y*dir2Y) * extendLength;

  // 绘制切线(可选,用于调试)
  stroke(255);
  line(lightX, lightY, tanPoint1X, tanPoint1Y);
  line(lightX, lightY, tanPoint2X, tanPoint2Y);

  // 绘制半透明阴影区域
  noStroke();
  fill(0, 0, 0, 100);
  beginShape();
    vertex(tanPoint1X, tanPoint1Y);
    vertex(shadowEnd1X, shadowEnd1Y);
    vertex(shadowEnd2X, shadowEnd2Y);
    vertex(tanPoint2X, tanPoint2Y);
  endShape(CLOSE);
}

function mouseMoved() {
  redraw();
}

代码说明

  1. 切线延伸逻辑:通过计算切点到光源的方向向量,归一化后乘以足够大的长度,确保延伸线能覆盖整个屏幕范围。
  2. 阴影区域构建:阴影是一个四边形,由两个切点和两个延伸端点组成,半透明黑色填充模拟真实阴影效果。
  3. 可扩展性:修改circleCenterX、circleCenterY变量即可移动圆形,鼠标位置直接对应光源位置,拖动鼠标就能看到阴影实时变化。

内容的提问来源于stack exchange,提问作者Sophie Tooke

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 23:36:15