逆向工程 之 随机颜色生成器代码解析与重构

1. 项目来源与简介

本项目来源于GitHub开源仓库:github wanghao221/moyu/
该项目是一个纯前端的的单页面应用,可以随机生成一个十六进制颜色值,并在页面中进行预览和一键复制,整体结构较为简单,代码量轻薄,适宜进行逆向工程探索。

  • 核心功能:点击按钮随机生成颜色,支持一键复制颜色代码
  • 技术栈:纯原生HTML5+CSS3+Vanilla+JavaScript,无任何框架依赖。
  • 文件结构:单一HTML文件,CSS与JS嵌入
  • 设计风格:主要颜色生成器卡片居中,底部背景图映衬

2. 运行环境及截图

运行环境
  • 操作系统:任意
  • 运行方式:浏览器直接打开.html文件
  • 测试浏览器:任意
  • 网络依赖:Google Fonts字体(可离线替换)+背景图(Haiyong.site)
源代码运行结果

image
如上图所示,浏览器打开运行文件,点击按钮Generate跳动展示随机生成的颜色于正中间卡片,点击Copy选项没有任何反应

展开查看核心实现代码
		let hexString = "0123456789abcdef";

		let genHexCode = () => {
			let hexCode = "#";
			for (i = 0; i < 6; i++) {
				hexCode += hexString[Math.floor(Math.random() * hexString.length)];
			}
			output.value = hexCode;
			outputColor.classList.remove("show-color");
			setTimeout(() => {
				outputColor.classList.add("show-color");
			}, 10);
			outputColor.style.backgroundColor = hexCode;
		}
作者通过定义变量存储所有十六进制字符串,后通过箭头函数,使用多个for循环,每次循环从hexString中选择字符追加到hexCode,用于随机生成颜色。
完整源代码如下
点击查看完整源代码
<!DOCTYPE html>
<html lang="en">
	<!-- Design by foolishdeveloper.com -->
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title>海拥 | 随机颜色生成器</title>
		<link rel="shortcut icon" href="http://haiyong.site/img/favicon.png">
	</head>
	<body>

		<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@600&display=swap" rel="stylesheet">
		<!-- Stylesheet -->
		<style media="screen">
			*{
    padding: 0;
    margin: 0;
    box-sizing: border-box;
    border: none;
    outline: none;
    font-family: sans-serif;
}
body{
    background: url("https://haiyong.site/img/bizhi/220808.png" );
	background-repeat: no-repeat;
	background-size: cover;
	height: 100vh;
}
.container{
    background-color: white;
    width: 310px;
    padding: 2.5em 1.1em;
    position: absolute;
    transform: translate(-50%,-50%);
    top: 50%;
    left: 50%;
    font-size: 16px;
    border-radius: 10px;
}
.container h1{
  font-size: 27px;
  text-align: center;
  margin-top: -20px;
  color: #09599a;
  margin-bottom: 20px;
}
#output-color{
    position: relative;
    height: 165px;
    width: 100%;
     box-shadow: 0 0 20px rgba(0,139,253,0.25);
    border: 2px solid #ffffff;
    margin: auto;
    display: grid;
    margin-bottom: 15px;
    place-items: center;
}
#output-color span{
    display: block;
    width: 100%;
    height: 100%;

}
.show-color{
    animation: pop 0.8s;
}
@keyframes pop{
    0%{
        transform: scale(0);
    }
    100%{
        transform: scale(1);
    }
}

input[type="text"]{
    width: 100%;
    background-color: transparent;

    box-shadow: 0 0 20px rgba(0,139,253,0.65);
    font-size: 1.3em;
    padding: 0.3em 0;

    margin: 1em 0;
    border-radius: 5px;
    color: #000000;
    text-align: center;
}
input[type="text"]::-moz-selection{
    background: transparent;
}
input[type="text"]::selection{
    background: transparent;
}

.btns{
    display: flex;
    margin-top: 15px;
    justify-content: space-around;
}
.btns button{
    font-size: 1.03em;
    padding: 0.8em 1.7em;
    border-radius: 7px;
    width: 120px;
    font-weight: 600;
    cursor: pointer;
}
#gen-btn{
    background-color: #205e94;
    color: #ffffff;
}
#copy-btn{

    background-color: #d23332;
    color: #ffffff;
}
.page-footer {
  position: fixed;
  right: 35px;
  bottom: 20px;
  display: flex;
  align-items: center;
  padding: 5px;
  color: black;
  background: rgba(255, 255, 255, 0.65);
}

.page-footer a {
  display: flex;
  margin-left: 4px;
}
.touxiang{
	bottom: 0px;
	width:30px;
	height:30px;
}

    </style>
		</head>
		<body>
			<div class="container">
				<h1>Color Generator</h1>
				<div id="output-color">
					<span></span>
				</div>

				<input type="text" id="output" readonly>
				<div class="btns">
					<button id="gen-btn">Generate</button>
					<button id="copy-btn">Copy</button>
				</div>
			</div>
			<!-- Script -->
			<script type="text/javascript">
				let outputColor = document.querySelector("#output-color span");
				let output = document.getElementById("output");
				let genBtn = document.getElementById("gen-btn");
				let copyBtn = document.getElementById("copy-btn");

				let hexString = "0123456789abcdef";

				let genHexCode = () => {
					let hexCode = "#";
					for (i = 0; i < 6; i++) {
						hexCode += hexString[Math.floor(Math.random() * hexString.length)];
					}
					output.value = hexCode;
					outputColor.classList.remove("show-color");
					setTimeout(() => {
						outputColor.classList.add("show-color");
					}, 10);
					outputColor.style.backgroundColor = hexCode;
				}

				copyBtn.addEventListener("click", () => {
					output.select();
					document.execCommand("copy");

				})

				window.onload = genHexCode;
				genBtn.addEventListener("click", genHexCode);
			</script>
			<footer class="page-footer">
			  <span>made by  </span>
			  <a href="https://haiyong.site/moyu" target="_blank">
				<img class="touxiang"  src="https://haiyong.site/img/favicon.png" alt="George Martsoukos logo">
			  </a>
			</footer>
		</body>
</html>

3. 主要问题与改善思路

  • Bug/规范:源代码循环变量未声明,在小工程上或许影响不大,在大工程中可能会导致全局变量污染。采用位运算+padStart的算法替代字符串拼接循环,可以解决该问题。
点击查看源代码
let hexString = "0123456789abcdef";
let genHexCode = () => {
	let hexCode = "#";
	for (i = 0; i < 6; i++) {
		hexCode += hexString[Math.floor(Math.random() * hexString.length)];
	}
	output.value = hexCode;
	outputColor.classList.remove("show-color");
	setTimeout(() => {
		outputColor.classList.add("show-color");
		}, 10);
		outputColor.style.backgroundColor = hexCode;
				}
点击查看改进后代码
function generateColor() {
  const hex = '#' +
    Math.floor(
      Math.random() * 0xFFFFFF
    )
    .toString(16)
    .padStart(6, '0')
    .toUpperCase();

  colorSpan.style.backgroundColor
    = hex;
  output.value = hex;
}
  • 用户体验:原始版本复制后页面没有任何变化,用户无法确认是否复制成功,体验较差。可以新增提示元素,复制成功后短暂显示已复制成功。
点击查看源代码
copyBtn.addEventListener(
  "click", () => {
    output.select();
    document.execCommand("copy");
  }
点击查看改进后代码
#toast {
  text-align: center;
  font-size: 13px;
  color: #16a085;
  height: 20px;
  opacity: 0;
  transition: opacity 0.3s ease;

}
<div id="toast"></div>
function showToast(msg) {
  toast.textContent = msg;

  toast.style.opacity = '1';
  setTimeout(() => {
    toast.style.opacity = '0';
  }, 2000);
}
  • 可读性:原始颜色生成代码采用随机字符拼接,逻辑分散,可读性较差,不易理解。利用 Math.random() * 0xFFFFFF 直接生成随机整数,再转十六进制,配合 padStart 补位,3行完成相同功能,语义更清晰。
点击查看源代码
let hexString =
  "0123456789abcdef";
let genHexCode = () => {
  let hexCode = "#";
  for (i = 0; i < 6; i++) {
    hexCode += hexString[
      Math.floor(
        Math.random()
        * hexString.length
      )
    ];
  }
点击查看改进后代码
function generateColor() {
  const hex = '#' +
    Math.floor(
      Math.random() * 0xFFFFFF
    ).toString(16)
     .padStart(6, '0')
     .toUpperCase();
}
  • 可靠性:背景图依赖个人管理器,一旦域名失效或网络不通将背景消失,变为白色。改用纯 CSS 渐变背景,色块过渡改用 transition,全程无外部依赖。值得说明的是,这样改进后视觉效果并不如原来,但作为软件工程逆向改进,为维护可靠,这似乎也可作为一个点。。。。。
点击查看源代码
body {
  background: url(
    "https://haiyong.site/img/...
     bizhi/220808.png"
  );
  background-repeat: no-repeat;
  background-size: cover;
  height: 100vh;
}
/* 动画:remove + setTimeout */
.show-color {
  animation: pop 0.8s;
}
@keyframes pop {
  0%   { transform: scale(0); }
  100% { transform: scale(1); }
}
点击查看改进后代码
/* CSS:纯渐变背景,无外部依赖 */
body {
  background: linear-gradient(
    135deg,
    #1a1a2e 0%,
    #16213e 50%,
    #0f3460 100%
  );
  min-height: 100vh;
}
/* 色块:用 transition 替代 animation */
#color-span {
  transition: background-color
    0.5s ease;
}
  • 功能增强:原始版本只显示HEX的颜色码,虽说作为一个摸鱼小游戏此功能已足够,但若设计师在玩耍设计工具中有时会需要RGB值,需要手动换算,不够便捷。可以增加 HEX → RGB 转换函数,在 UI 新增 R/G/B 分量展示区,与颜色生成同步更新。
点击查看代码
/* HTML:只有一个 input 显示 HEX */
<input type="text"
  id="output" readonly>
// JS:只赋值 HEX
output.value = hexCode;
点击查看改进后代码

<div class="color-values">
  <div><span>R</span>
    <span id="r-val"></span></div>
  <div><span>G</span>
    <span id="g-val"></span></div>
  <div><span>B</span>
    <span id="b-val"></span></div>
</div>

const r = parseInt(hex.slice(1,3),16);
const g = parseInt(hex.slice(3,5),16);
const b = parseInt(hex.slice(5,7),16);
rVal.textContent = r;
gVal.textContent = g;
bVal.textContent = b;

4. 新代码(改进版)

点击查看改进后完整代码
<!DOCTYPE html>
<html lang="en">
	<!-- Design by foolishdeveloper.com -->
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title>海拥 | 随机颜色生成器</title>
		<link rel="shortcut icon" href="http://haiyong.site/img/favicon.png">
		<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@600&display=swap" rel="stylesheet">
	</head>
	<body>
		<style media="screen">
			* {
				padding: 0;
				margin: 0;
				box-sizing: border-box;
				border: none;
				outline: none;
				font-family: sans-serif;
			}

			body {
				background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
				background-repeat: no-repeat;
				background-size: cover;
				height: 100vh;
			}

			.container {
				background-color: white;
				width: 310px;
				padding: 2.5em 1.1em;
				position: absolute;
				transform: translate(-50%, -50%);
				top: 50%;
				left: 50%;
				font-size: 16px;
				border-radius: 10px;
			}

			.container h1 {
				font-size: 27px;
				text-align: center;
				margin-top: -20px;
				color: #09599a;
				margin-bottom: 20px;
			}

			#output-color {
				position: relative;
				height: 165px;
				width: 100%;
				box-shadow: 0 0 20px rgba(0, 139, 253, 0.25);
				border: 2px solid #ffffff;
				margin: auto;
				display: grid;
				margin-bottom: 15px;
				place-items: center;
			}

			#output-color span {
				display: block;
				width: 100%;
				height: 100%;
				transition: background-color 0.5s ease;
			}

			input[type="text"] {
				width: 100%;
				background-color: transparent;
				box-shadow: 0 0 20px rgba(0, 139, 253, 0.65);
				font-size: 1.3em;
				padding: 0.3em 0;
				margin: 1em 0 0.5em;
				border-radius: 5px;
				color: #000000;
				text-align: center;
			}

			input[type="text"]::-moz-selection { background: transparent; }
			input[type="text"]::selection { background: transparent; }

			.color-values {
				display: flex;
				justify-content: space-between;
				gap: 6px;
				margin-bottom: 10px;
			}

			.color-value-box {
				flex: 1;
				background: #f4f4f4;
				border-radius: 5px;
				text-align: center;
				padding: 5px 2px;
			}

			.color-value-box .label {
				display: block;
				font-size: 10px;
				color: #999;
				letter-spacing: 1px;
				margin-bottom: 2px;
			}

			.color-value-box .val {
				font-family: 'Roboto Mono', monospace;
				font-size: 13px;
				font-weight: 600;
				color: #333;
			}

			#toast {
				text-align: center;
				font-size: 12px;
				color: #16a085;
				font-weight: 600;
				height: 18px;
				margin-bottom: 6px;
				opacity: 0;
				transition: opacity 0.3s ease;
				font-family: 'Roboto Mono', monospace;
			}

			.btns {
				display: flex;
				margin-top: 15px;
				justify-content: space-around;
			}

			.btns button {
				font-size: 1.03em;
				padding: 0.8em 1.7em;
				border-radius: 7px;
				width: 120px;
				font-weight: 600;
				cursor: pointer;
			}

			#gen-btn  { background-color: #205e94; color: #ffffff; }
			#copy-btn { background-color: #d23332; color: #ffffff; }

			.page-footer {
				position: fixed;
				right: 35px;
				bottom: 20px;
				display: flex;
				align-items: center;
				padding: 5px;
				color: black;
				background: rgba(255, 255, 255, 0.65);
			}

			.page-footer a {
				display: flex;
				margin-left: 4px;
			}

			.touxiang {
				bottom: 0px;
				width: 30px;
				height: 30px;
			}
		</style>

		<div class="container">
			<h1>Color Generator</h1>

			<div id="output-color">
				<span></span>
			</div>

			<input type="text" id="output" readonly>

			<div class="color-values">
				<div class="color-value-box">
					<span class="label">R</span>
					<span class="val" id="r-val">—</span>
				</div>
				<div class="color-value-box">
					<span class="label">G</span>
					<span class="val" id="g-val">—</span>
				</div>
				<div class="color-value-box">
					<span class="label">B</span>
					<span class="val" id="b-val">—</span>
				</div>
			</div>

			<div id="toast"></div>

			<div class="btns">
				<button id="gen-btn">Generate</button>
				<button id="copy-btn">Copy</button>
			</div>
		</div>

		<script type="text/javascript">
			let outputColor = document.querySelector("#output-color span");
			let output      = document.getElementById("output");
			let genBtn      = document.getElementById("gen-btn");
			let copyBtn     = document.getElementById("copy-btn");
			let toast       = document.getElementById("toast");
			let rVal        = document.getElementById("r-val");
			let gVal        = document.getElementById("g-val");
			let bVal        = document.getElementById("b-val");

			let genHexCode = () => {
				let hexCode = "#" + Math.floor(Math.random() * 0xFFFFFF)
					.toString(16)
					.padStart(6, "0")
					.toUpperCase();

				output.value = hexCode;
				outputColor.style.backgroundColor = hexCode;

				let r = parseInt(hexCode.slice(1, 3), 16);
				let g = parseInt(hexCode.slice(3, 5), 16);
				let b = parseInt(hexCode.slice(5, 7), 16);
				rVal.textContent = r;
				gVal.textContent = g;
				bVal.textContent = b;
			}

			copyBtn.addEventListener("click", () => {
				output.select();
				document.execCommand("copy");
				showToast("✓ 已复制 " + output.value);
			});

			function showToast(msg) {
				toast.textContent = msg;
				toast.style.opacity = "1";
				setTimeout(() => { toast.style.opacity = "0"; }, 2000);
			}

			window.onload = genHexCode;
			genBtn.addEventListener("click", genHexCode);
		</script>

		<footer class="page-footer">
			<span>made by </span>
			<a href="https://haiyong.site/moyu" target="_blank">
				<img class="touxiang" src="https://haiyong.site/img/favicon.png" alt="George Martsoukos logo">
			</a>
		</footer>
	</body>
</html>

5. 重构后测试

如下图所示重构后的颜色随机生成器,在点击copy后会有短暂友好提示,并且有颜色提示,可供设计者便携使用,不再依赖个人管理器,背景图片被改掉。但值得强调,此次改进后结果带来的视觉冲击力远不如原版本好,此处改动需斟酌选取。
屏幕截图 12026-03-05 191152
屏幕截图2 2026-03-05 191413

6. 总结与思考

  • 关于逆向软件工程:所谓逆向软件工程并非一味复制代码,魔改代码,而是在接手新代码后理解设计意图,读懂代码,发现潜在问题,用更好的方式进行改进。
    而本次改变,对于背景图的换掉,作为一个摸鱼小游戏完全失去其本有的观赏性,有违设计意图。其余改动,尚且可以,符合逆向软件工程特点。
  • 关于项目改进难点:在何角度进行有效改动为本项目的难点,也花费时间较长,需仔细阅读理解代码,在可读性、可靠性、可维护性、用户友好性、功能增强等等方面进行考虑,可有效找到优化点,可进一步扩展总结。
  • 关于项目的选择:本项目是作者为数不多的在github上扒代码,从寻找到下载一步步,索性作为单一前端展示页面运行复现优化较为简单,不涉及大量环境问题,且项目较小,较为满意。
posted @ 2026-03-05 19:35  Lunanan  阅读(0)  评论(0)    收藏  举报