逆向工程 之 随机颜色生成器代码解析与重构
1. 项目来源与简介
本项目来源于GitHub开源仓库:github wanghao221/moyu/。
该项目是一个纯前端的的单页面应用,可以随机生成一个十六进制颜色值,并在页面中进行预览和一键复制,整体结构较为简单,代码量轻薄,适宜进行逆向工程探索。
- 核心功能:点击按钮随机生成颜色,支持一键复制颜色代码
- 技术栈:纯原生HTML5+CSS3+Vanilla+JavaScript,无任何框架依赖。
- 文件结构:单一HTML文件,CSS与JS嵌入
- 设计风格:主要颜色生成器卡片居中,底部背景图映衬
2. 运行环境及截图
运行环境
- 操作系统:任意
- 运行方式:浏览器直接打开.html文件
- 测试浏览器:任意
- 网络依赖:Google Fonts字体(可离线替换)+背景图(Haiyong.site)
源代码运行结果

如上图所示,浏览器打开运行文件,点击按钮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;
}
完整源代码如下
点击查看完整源代码
<!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后会有短暂友好提示,并且有颜色提示,可供设计者便携使用,不再依赖个人管理器,背景图片被改掉。但值得强调,此次改进后结果带来的视觉冲击力远不如原版本好,此处改动需斟酌选取。


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

浙公网安备 33010602011771号