javascript - SVG 月相

标签 javascript svg astronomy

对于较大的项目,我想在悬停日期添加月相。我找到了计算月相的javascript,我还找到了@Alexandr_TT在Create waxing crescent moon in svg using path发布的SVG月相解决方案.

JavaScript 以数学方式计算月相:

function getMoonPhase(year, month, day) {
    var c = e = jd = b = 0;

    if (month < 3) {
    year--;
    month += 12;
    }
    ++month;
    c = 365.25 * year;
    e = 30.6 * month;
    jd = c + e + day - 694039.09; //jd is total days elapsed
    jd /= 29.5305882; //divide by the moon cycle
    b = parseInt(jd); //int(jd) -> b, take integer part of jd
    jd -= b; //subtract integer part to leave fractional part of original jd
    b = Math.round(jd * 8); //scale fraction from 0-8 and round
    if (b >= 8 ) {
        b = 0; //0 and 8 are the same so turn 8 into 0
    }

    // 0 => New Moon
    // 1 => Waxing Crescent Moon
    // 2 => Quarter Moon
    // 3 => Waxing Gibbous Moon
    // 4 => Full Moon
    // 5 => Waning Gibbous Moon
    // 6 => Last Quarter Moon
    // 7 => Waning Crescent Moon

    return b;
}

Alexandr的SVG是一个动画,所以我的问题是svg可以是基于javascript输出值的快照吗?

我当然可以为 8 个阶段中的每个阶段生成单独的静态 SVG。我还可以尝试修改我在 Github 上找到的解决方案 https://github.com/tingletech/moon-phase ,但我对 Alexandr 的 SVC 代码很感兴趣,因为它的尺寸很小。

我的目标是输出 javascript 的数值,然后以实现目标所需的最少代码量呈现视觉驱动的 SVG。

我是 SVG 新手。 github上的解决方案使用了SVG中path的arc属性,看起来Alexandr使用的是两个相交的圆。能否根据我从 javascript 获取的值有效停止动画?

提前致谢。

最佳答案

您好,我有一个如下的工作副本:

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
	<meta charset="utf-8">
	<title>Moon Phase Today</title>
</head>
<body>
	<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"  viewBox="0 0 73 73" >
		<defs>
			<radialGradient id="RadialGrad" fx="50%" fy="50%" r="65%" spreadMethod="pad">
				<stop offset="0%"   stop-color="#E7D68C" stop-opacity="1"/>
				<stop offset="100%" stop-color="#FFFEED" stop-opacity="1" />
			</radialGradient>
			
		</defs>
		<rect width="100%" height="100%" />
		<g transform="rotate(-20 35.5 35.5)">
			<circle cx="35.5" cy="35.5" r="35" stroke="none"  fill="url(#RadialGrad)" />
			
			<circle id='layoverCircle' cx="35.5" cy="35.5" r="35" stroke="none" fill="black" />
			
			<rect id="layoverRectangle" style="display: none" width="100%" height="100%" />
				<!-- <animate id="youngMoon" attributeName="cx" values="35.5;-35.5;" begin="1s;oldMoon.end+1s" dur="10s" fill="freeze" /> -->
				<!-- <animate id="oldMoon" attributeName="cx" values="105;35.5;" begin="youngMoon.end+1s" dur="10s"  fill="freeze" />  -->
				
			<!-- </circle>  -->
		</g>
	</svg>
	<script type="text/javascript">
		function setState(value, showCircle, showRect) {
			
			let circle = document.getElementById('layoverCircle');
			let rect = document.getElementById('layoverRectangle');
			
			circle.style.display = showCircle ? "block" : "none";
			rect.style.display = showRect ? "block" : "none";
			
			if (showRect) rect.style.transform = value
			if (showCircle) circle.setAttribute("cx", value);
		}
		
		function getMoonPhase(year, month, day) {
			var c = e = jd = b = 0;
			
			if (month < 3) {
				year--;
				month += 12;
			}
			++month;
			c = 365.25 * year;
			e = 30.6 * month;
			jd = c + e + day - 694039.09; //jd is total days elapsed
			jd /= 29.5305882; //divide by the moon cycle
			b = parseInt(jd); //int(jd) -> b, take integer part of jd
			jd -= b; //subtract integer part to leave fractional part of original jd
			b = Math.round(jd * 8); //scale fraction from 0-8 and round
			if (b >= 8 ) {
				b = 0; //0 and 8 are the same so turn 8 into 0
			}
			
			
			// 0 => New Moon 37.5 [show circle, hide rect]
			// 1 => Waxing Crescent Moon 50.5 [show circle, hide rect]
			// 2 => Quarter Moon //translateX(50%), [display rect, hide circle]
			// 3 => Waxing Gibbous Moon 70.5 [show circle, hide rect]
			// 4 => Full Moon [hide circle and rect]
			// 5 => Waning Gibbous Moon, -15.5 [show circle, hide rect]
			// 6 => Last Quarter Moon //transform: translateX(-50%) [display rect, hide circle]
			// 7 => Waning Crescent Moon 30.5 [show circle, hide rect]
			
			return b;
		}
		
		let d = new Date();
		let i = 0;
		let callback = function () {			
			let phase = getMoonPhase(d.getFullYear(), d.getMonth()+1, d.getDate() + i)
			i += 4
			if (phase == 0) setState(37.5, true, false);
			if (phase == 1) setState(50.5, true, false);
			if (phase == 2) setState("translateX(50%)", false, true);
			if (phase == 3) setState(70.5 , true, false);
			if (phase == 4) setState(NaN , false, false);
			if (phase == 5) setState(-15.5, true, false);
			if (phase == 6) setState("translateX(-50%)", false, true);
			if (phase == 7) setState(30.5 , true, false);
			setTimeout(callback, 1000);
		};
		callback();
	</script>
</body>
</html>

setState 函数基本上就是您所寻找的。您还可以根据您的审美需求更改 setState 中的值。

关于javascript - SVG 月相,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57063248/

相关文章:

javascript - 使用 svg 图像显示进度动画

java - 检查两个 svg 文件是否相同

python - 使用 Python 的耦合微分方程

python - 任何人都可以帮助修复 Google Colaboratory 上的这个常量 "ModuleNotFoundError"吗?

javascript - 在 angularJS 中覆盖模块值/常量的最佳方法

javascript - 如何用Javascript引用CSS动画?

javascript - AngularJS 指令调用另一个指令未正确传递数据以使用 $compile 进行渲染

javascript - 如何在不使用按钮的情况下加载页面?

html - 为什么这个 SVG 可以在浏览器中以原始方式查看,但不能在网页中查看?

python - Pyephem Alt。和 Az 是完全错误的