我正在开发一个 React/TypeScript 组件,它根据 0 到 1 之间的值渲染月相。问题是月相在第一季度和最后四分之一阶段之间无法正确渲染。具体来说,该组件将月亮显示为盈盈,而本应盈亏,反之亦然。
我尝试使用 Canvas 而不是 SVG,但没有解决问题。我还尝试将月球可见部分的计算更改为:
const visiblePortion = Math.cos(normalizedPhase * Math.PI * 2);
但问题并没有消失。在这些相位中,月亮的明/暗部分仍然是相反的,而不是在 0.25 和 0.75 之间正确渲染月亮。
月相以 0 到 1 之间的数字表示,其中:
我希望月亮在 0 到 0.5 之间逐渐盈,然后在 0.5 到 1 之间逐渐亏,但对于 0.25 到 0.75 之间的相位,明暗部分会交换。
这是我正在使用的代码:
import React from "react";
interface MoonPhaseProps {
phase: number;
moonTexture?: string;
rotation?: number;
}
const MoonPhase = ({
phase,
moonTexture = "/image/moon/moon.svg",
rotation = 0,
}: MoonPhaseProps) => {
// Ensure phase is between 0 and 1
const normalizedPhase = Math.max(0, Math.min(phase, 1));
console.log(phase, normalizedPhase);
// Convert phase to radians
const phaseRadians = normalizedPhase * 2 * Math.PI;
// Determine if it's a waxing or waning phase
const isWaxing = normalizedPhase <= 0.5;
// Calculate the visible portion of the moon
const visiblePortion = Math.cos(phaseRadians);
// Adjust the path for waxing and waning phases
const pathD = isWaxing
? `M50,0 A50,50 0 1,1 50,100 A${
50 * Math.abs(visiblePortion)
},50 0 1,0 50,0`
: `M50,0 A50,50 0 1,0 50,100 A${
50 * Math.abs(visiblePortion)
},50 0 1,1 50,0`;
return (
<svg
width="100%"
viewBox="0 0 100 100"
data-phase={phase}
style={{
transform: `rotate(${rotation}deg)`,
transition: "transform 0.3s ease-in-out",
}}
>
<defs>
<pattern
id="moonTexturePattern"
patternUnits="userSpaceOnUse"
width="100"
height="100"
>
<image href={moonTexture} x="0" y="0" width="100" height="100" />
</pattern>
<mask id="moonMask">
<rect x="0" y="0" width="100" height="100" fill="white" />
<path d={pathD} fill="black" />
</mask>
</defs>
{/* Moon texture */}
<circle cx="50" cy="50" r="49" fill="url(#moonTexturePattern)" />
{/* Shading for the dark side of the moon */}
<circle
cx="50"
cy="50"
r="49"
fill="rgba(0,0,0,0.7)"
mask="url(#moonMask)"
/>
</svg>
);
};
export default React.memo(MoonPhase);
提前致谢。
您希望月球的可见部分根据 cos(相位)规则从 0% 变化到 100%,但您未能检查角度转换中的端点是否有意义。您似乎想要:
cos(0) = 1, cos (pi/2) = 0, cos(pi) = -1
但是您可以将相位角(以弧度为单位)设置为乘以 2*pi :
const visiblePortion = Math.cos(normalizedPhase * Math.PI * 2);
应该这样读
const visiblePortion = Math.cos(normalizedPhase * Math.PI);
可见部分的符号决定隐藏哪一侧。
然后它应该要么正是你想要的,要么相反(在这种情况下是 s/cos/sin/)。