You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何重构Etch-A-Sketch JS代码实现DRY?彩虹模式异常排查

解决Etch-A-Sketch的代码重复与Rainbow模式颜色问题

让我们一步步解决你的问题,先理清核心问题所在,再重构代码遵循DRY原则并修复颜色bug:

问题根源分析

  1. Rainbow模式颜色不随机:你当前调用pickColor('rgb(' + randColor() + ')')时,randColor()只会执行一次,生成一个固定颜色传给pickColor,所有网格的mouseover事件都会复用这个颜色。我们需要每个mouseover触发时实时生成新颜色。
  2. 代码重复:eraseGrid()和drawRainbow()的逻辑几乎完全一致,只是操作的按钮和颜色规则不同,完全可以抽象成公共函数。

重构后的完整代码

/**************************** Input->Button DOM ****************************/
const newGrid = document.getElementById('new-grid');
newGrid.addEventListener('click', createGrid);

const erase = document.getElementById('erase');
erase.addEventListener('click', () => toggleMode(erase, 'erase', rainbow, 'rainbow', '#f2f2f2'));

const rainbow = document.getElementById('rainbow');
rainbow.addEventListener('click', () => toggleMode(rainbow, 'rainbow', erase, 'erase', getRandomColor));

/*********************** Grid variable and creation ***********************/
const main = document.querySelector('main');

// 初始化16x16网格
drawGrid(16, ((600 / 16) - 2) + 'px');
setGridColor('#333');

// 创建新网格
function createGrid() {
    // 移除现有网格元素(从后往前删避免索引混乱)
    for (let i = main.childNodes.length - 1; i >= 0 ; i--) {
        main.removeChild(main.childNodes[i]);
    }

    let size;
    do {
        size = parseInt(prompt("Please enter a number from 1 to 64", ""), 10);
    } while(Number.isNaN(size) || size > 64 || size < 1);

    const numPx = (600 / size) - 2;
    const px = numPx + 'px';
    drawGrid(size, px);
    setGridColor('#333');
}

// 设置网格颜色:支持固定颜色值或返回颜色的函数
function setGridColor(colorOrFunc) {
    // 先移除所有旧的mouseover事件(避免重复绑定)
    Array.from(main.children).forEach(cell => {
        if (cell.classList.contains('clear')) return; // 跳过清除浮动的div
        cell.removeEventListener('mouseover', handleCellHover);
        cell.addEventListener('mouseover', handleCellHover);
    });

    // 处理hover的逻辑函数
    function handleCellHover() {
        const color = typeof colorOrFunc === 'function' ? colorOrFunc() : colorOrFunc;
        this.style.backgroundColor = color;
    }
}

// 绘制网格
function drawGrid(size, px) {
    for (let i = 0; i < size; i++) {
        for (let j = 0; j < size; j++) {
            const div = document.createElement('div');
            main.appendChild(div);
            div.setAttribute('style', `width: ${px}; height: ${px}; float: left; border: 1px solid #333;`);
        }
    }
    // 清除浮动的div
    const clearDiv = document.createElement('div');
    clearDiv.setAttribute('class', 'clear');
    main.appendChild(clearDiv);
}

// 抽象模式切换逻辑:处理按钮激活状态+设置网格颜色规则
function toggleMode(activeBtn, activeClass, inactiveBtn, inactiveClass, colorRule) {
    activeBtn.classList.toggle(activeClass);
    inactiveBtn.classList.remove(inactiveClass);

    if (activeBtn.classList.contains(activeClass)) {
        setGridColor(colorRule);
    } else {
        // 切换回默认黑色
        setGridColor('#333');
    }
}

// 生成随机RGB颜色字符串
function getRandomColor() {
    const r = Math.floor(Math.random() * 256);
    const g = Math.floor(Math.random() * 256);
    const b = Math.floor(Math.random() * 256);
    return `rgb(${r}, ${g}, ${b})`;
}

关键改进点说明

  1. 修复Rainbow颜色随机问题:

    • 把pickColor改名为setGridColor,它现在可以接受固定颜色值或者返回颜色的函数。
    • Rainbow模式传入getRandomColor函数,每次mouseover触发时,都会调用这个函数生成新的随机颜色,而不是用固定值。
    • 新增handleCellHover内部函数,负责在hover时动态获取颜色。
  2. 消除代码重复(遵循DRY原则):

    • 抽象出toggleMode函数,统一处理按钮的激活状态切换(添加/移除class)、取消其他按钮的激活状态,以及设置对应的颜色规则。
    • erase和rainbow按钮的点击事件现在只需要调用toggleMode,传入对应的参数即可,无需重复写逻辑。
  3. 额外优化:

    • 在setGridColor中先移除旧的mouseover事件,避免重复绑定导致多次触发颜色变化。
    • 简化getRandomColor函数,直接返回RGB字符串,无需数组拼接。
    • 给清除浮动的div命名更清晰(clearDiv),避免变量名冲突。

这样修改后,不仅解决了代码重复问题,Rainbow模式也能实现每个网格mouseover时生成不同的随机颜色了。

内容的提问来源于stack exchange,提问作者user7217977

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:59:59