基于HTML/CSS/JS的动态元素周期表

举报
海拥 发表于 2022/01/18 16:41:37 2022/01/18
【摘要】 🌊 作者主页:海拥🌊 简介:🏆CSDN全栈领域优质创作者、🥇HDZ核心组成员、🥈蝉联C站周榜前十🌊 粉丝福利:粉丝群 每周送6~9本书,不定期送各种小礼品,往期获奖公布 基于HTML/CSS/JS的动态元素周期表一个基于HTML,CSS,JS的动态元素周期表,文章中给出了完整的源码 演示效果 演示地址https://wanghao221.github.io/game/yuans...

🌊 作者主页:海拥
🌊 简介:🏆CSDN全栈领域优质创作者、🥇HDZ核心组成员、🥈蝉联C站周榜前十
🌊 粉丝福利:粉丝群 每周送6~9本书,不定期送各种小礼品,往期获奖公布

基于HTML/CSS/JS的动态元素周期表

一个基于HTML,CSS,JS的动态元素周期表,文章中给出了完整的源码


演示效果

在这里插入图片描述

演示地址

https://wanghao221.github.io/game/yuansuzhouqibiao/

(打不开的话刷新一下试试)


代码展示

HTML

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Wanghao | 元素周期表</title>	
		<link rel="stylesheet" href="css/style.css">
			
	</head>
	<body>			
		<div id="container"></div>	
	</body>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.3.1/d3.min.js"></script>
	<script src="js/script.js"></script>
</html>

CSS

@import url("https://fonts.googleapis.com/css2?family=Merriweather:wght@700&display=swap");
body {
  margin: 0;
  justify-content: center;
  display: flex;
  background: #222831;
}

#container {
  width: 95vw;
  height: 55vw;
  background: #262c35;
  margin-top: 5vw;
  position: relative;
  border-radius: 5px;
  margin-bottom: 5vw;
}

.element-name {
  position: absolute;
  left: 50%;
  top: 40%;
  transform: translate(-50%, -50%);
  font-size: 1.5vw;
  font-family: "Merriweather", serif;
  font-weight: 700;
}
.element-number {
  position: absolute;
  left: 50%;
  top: 15%;
  transform: translate(-50%, -50%);
  font-size: 0.6vw;
}

.legend {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-size: 0.8vw;
  font-family: "Merriweather", serif;
  font-weight: 700;
}

.svg {
  width: 100%;
  height: 100%;
}

JS

let elements;
const { PI, sin, cos, random } = Math;
const TAU = 2 * PI;
const range = (n, m = 0) =>
  Array(n)
    .fill(m)
    .map((i, j) => i + j);
const map = (value, sMin, sMax, dMin, dMax) => {
  return dMin + ((value - sMin) / (sMax - sMin)) * (dMax - dMin);
};
const polar = (ang, r = 1, [x = 0, y = 0] = []) => [
  x + r * cos(ang),
  y + r * sin(ang)
];
const container = d3.select("#container");

const setStyle = (el, attrs) =>
  Object.entries(attrs).reduce((acc, [key, val]) => acc.style(key, val), el);
const setAttrs = (el, attrs) =>
  Object.entries(attrs).reduce((acc, [key, val]) => acc.attr(key, val), el);

const clipCords = range(6).map((i) => {
  const ang = map(i, 0, 6, 0, TAU);
  return polar(ang + PI / 2, 50);
});
const clipPathD = `M${[...clipCords, clipCords[0]]
  .map(([x, y]) => `L${x},${y}`)
  .join("")
  .slice(1)}`;

const svgRoot = container.append("svg");
setAttrs(svgRoot, { width: "0px", height: "0px" });
const defs = svgRoot.append("defs");
const clipPath = defs.append("clipPath");
setAttrs(clipPath, { id: "clipPath" });
const clipPathPath = clipPath.append("path");
setAttrs(clipPathPath, { d: clipPathD });

class Atom {
  constructor(parent, color) {
    this.element = parent.append("circle");
    setAttrs(this.element, { cx: 0, cy: 0, r: 4, fill: `${color}88` });

    this.seed1 = random() * TAU;
    this.seed2 = random() * TAU;
  }

  updatePosition(t) {
    const cx = 25 * sin(this.seed1 + t);
    const cy = 25 * sin(this.seed2 + t);
    setAttrs(this.element, { cx, cy });
  }
}

class Element {
  constructor(x, y, name, number, phase, color) {
    this.root = container.append("div");
    setStyle(this.root, {
      width: "5vw",
      height: "5vw",
      transform: `translate(${x}vw, ${y}vw)`,
      position: "absolute"
    });

    this.phase = phase;

    this.svg = this.root.append("svg");
    setAttrs(this.svg, { viewBox: "0 0 100 100", class: "svg" });
    this.group = this.svg.append("g");
    setAttrs(this.group, { transform: "translate(50,50)" });

    this.border = this.group.append("path");
    setAttrs(this.border, { d: clipPathD, fill: "none", stroke: `${color}88` });

    if (phase === "Solid") {
      this.solid = this.group.append("rect");
      setAttrs(this.solid, {
        x: -50,
        y: 18,
        width: 100,
        height: 60,
        fill: `${color}88`,
        style: "clip-path: url(#clipPath)"
      });
    }

    if (phase === "Liquid") {
      this.liquidPathA = this.group.append("path");
      setAttrs(this.liquidPathA, {
        d: "",
        fill: `${color}88`,
        style: "clip-path: url(#clipPath)"
      });
      this.liquidPathB = this.group.append("path");
      setAttrs(this.liquidPathB, {
        d: "",
        fill: `${color}44`,
        style: "clip-path: url(#clipPath)"
      });
    }

    if (phase === "Gas") {
      this.atoms = range(5).map(() => new Atom(this.group, color));
    }

    this.name = this.root.append("div").text(name);
    setAttrs(this.name, { class: "element-name" });
    setStyle(this.name, { color: `${color}88` });
    this.number = this.root.append("div").text(number);
    setAttrs(this.number, { class: "element-number" });
    setStyle(this.number, { color: `${color}88` });
  }

  update(t, path1, path2) {
    if (this.phase === "Liquid") {
      this.updateLiquid(path1, path2);
    }
    if (this.phase === "Gas") {
      this.updateAtoms(t);
    }
  }

  updateLiquid(path1, path2) {
    setAttrs(this.liquidPathA, { d: path1 });
    setAttrs(this.liquidPathB, { d: path2 });
  }

  updateAtoms(t) {
    this.atoms.forEach((atom) => {
      atom.updatePosition(t);
    });
  }
}

const categoryColors = {
  "diatomic nonmetal": "#3d7ea6",
  "noble gas": "#bc6ff1",
  "alkali metal": "#f05454",
  "alkaline earth metal": "#ffa36c",
  metalloid: "#64958f",
  "polyatomic nonmetal": "#8d93ab",
  "post-transition metal": "#c0e218",
  "transition metal": "#fcf876",
  lanthanide: "#949cdf",
  actinide: "#16697a"
};

function createElements(data) {
  elements = data.map((element, index) => {
    const category = element.category;
    const name = element.symbol;
    const number = element.number;
    const phase = element.phase;
    const ix = element.xpos;
    const iy = element.ypos;
    const x = ix * 4.8 + ((iy + 1) % 2) * 2.5 - 2;
    const y = iy * 4.5 - 4;
    const color = categoryColors[category] || "#93abd3";

    return new Element(x, y, name, number, phase, color);
  });
}

let step = 0;

function animate() {
  step = (step + 1) % 100;
  const t = map(step, 0, 100, 0, TAU);

  const curve1 = range(10)
    .map((i) => {
      const ang = map(i, 0, 10, 0, TAU);
      const x = map(i, 0, 10, -50, 50);
      const y = 10 + 4 * sin(ang + t);
      return `L${x},${y}`;
    })
    .join("");

  const curve2 = range(10)
    .map((i) => {
      const ang = map(i, 0, 10, 0, TAU);
      const x = map(i, 0, 10, -50, 50);
      const y = 10 + 6 * sin(ang + t + PI);
      return `L${x},${y}`;
    })
    .join("");

  const path1 = `M50,10L50,50L-50,50L-50,10${curve1}`;
  const path2 = `M50,10L50,50L-50,50L-50,10${curve2}`;

  elements.forEach((element) => {
    element.update(t, path1, path2);
  });

  requestAnimationFrame(animate);
}

fetch("https://assets.codepen.io/3685267/periodic-table-data.json")
  .then((response) => response.json())
  .then((data) => {
    createElements(data.elements);
    animate();
  });

以上就是所有代码了,CV一下这酷炫的动态元素周期表你也可以拥有,或者也可以用以下方式下载。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。