分页: 1 / 1

將改進混亂時鐘的網頁版

发表于 : 2026年 1月 31日 03:15
ejsoon
mark

Re: 將改進混亂時鐘的網頁版

发表于 : 2026年 2月 1日 15:08
ejsoon

代码: 全选

<svg id="etani" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" width="480" height="480" rfm="repeat"> <defs mode="1" rotatestep="1" rotatestart="0" stepvalue="180" snapto="0" displaywait="1">   <g id="tile0" frontfill="#ca5326" backfill="#2691c0"><path display="none" d="M 21,21 L -22,22 L -23,-23 Z"/>

<circle r="24" cx="0" cy="0" fill="white" class="bcenter0"/>

<text x="-9" y="17" stroke="none" fill="blue" font-size="48" font-family="ZT Nature">1</text>

</g><g id="tile1" frontfill="#ca5326" backfill="#2691c0"><path display="none" d="M 21,21 L -22,22 L -23,-23 Z"/>

<circle r="24" cx="0" cy="0" fill="white" class="bcenter0"/>

<text x="-14" y="16" stroke="none" fill="blue" font-size="48" font-family="ZT Nature">2</text>

</g><g id="tile2" frontfill="#ca5326" backfill="#2691c0"><path display="none" d="M 21,21 L -22,22 L -23,-23 Z"/>

<circle r="24" cx="0" cy="0" fill="white" class="bcenter0"/>

<text x="-14" y="17" stroke="none" fill="blue" font-size="48" font-family="ZT Nature">3</text>

</g><g id="tile3" frontfill="#ca5326" backfill="#2691c0"><path display="none" d="M 21,21 L -22,22 L -23,-23 Z"/>

<circle r="24" cx="0" cy="0" fill="white" class="bcenter0"/>

<text x="-16" y="17" stroke="none" fill="blue" font-size="48" font-family="ZT Nature">4</text>

</g><g id="tile4" frontfill="#ca5326" backfill="#2691c0"><path display="none" d="M 21,21 L -22,22 L -23,-23 Z"/>

<circle r="24" cx="0" cy="0" fill="white" class="bcenter0"/>

<text x="-14" y="17" stroke="none" fill="blue" font-size="48" font-family="ZT Nature">5</text>

</g><g id="tile5" frontfill="#ca5326" backfill="#2691c0"><path display="none" d="M 21,21 L -22,22 L -23,-23 Z"/>

<circle r="24" cx="0" cy="0" fill="white" class="bcenter0"/>

<text x="-15" y="16" stroke="none" fill="blue" font-size="48" font-family="ZT Nature">6</text>

</g><g id="tile6" frontfill="#ca5326" backfill="#2691c0"><path display="none" d="M 21,21 L -22,22 L -23,-23 Z"/>

<circle r="24" cx="0" cy="0" fill="white" class="bcenter0"/>

<text x="-14" y="18" stroke="none" fill="blue" font-size="48" font-family="ZT Nature">7</text>

</g><g id="tile7" frontfill="#ca5326" backfill="#2691c0"><path display="none" d="M 21,21 L -22,22 L -23,-23 Z"/>

<circle r="24" cx="0" cy="0" fill="white" class="bcenter0"/>

<text x="-15" y="16" stroke="none" fill="blue" font-size="48" font-family="ZT Nature">8</text>

</g>


<g id="tile8" frontfill="#ca5326" backfill="#2691c0"><path display="none" d="M 21,21 L -22,22 L -23,-23 Z"/>

<circle r="24" cx="0" cy="0" fill="white" class="bcenter0"/>

<text x="-14" y="16" stroke="none" fill="blue" font-size="48" font-family="ZT Nature">9</text>

</g><g id="tile9" frontfill="#ca5326" backfill="#2691c0"><path display="none" d="M 21,21 L -22,22 L -23,-23 Z"/>

<circle r="24" cx="0" cy="0" fill="white" class="bcenter0"/>

<text x="-20" y="14" stroke="none" fill="blue" font-size="40" font-family="ZT Nature">10</text>

</g><g id="tile10" frontfill="#ca5326" backfill="#2691c0"><path display="none" d="M 21,21 L -22,22 L -23,-23 Z"/>

<circle r="24" cx="0" cy="0" fill="white" class="bcenter0"/>

<text x="-16" y="14" stroke="none" fill="blue" font-size="40" font-family="ZT Nature">11</text>

</g><g id="tile11" frontfill="#ca5326" backfill="#2691c0"><path display="none" d="M 21,21 L -22,22 L -23,-23 Z"/>

<circle r="24" cx="0" cy="0" fill="white" class="bcenter0"/>

<text x="-21" y="14" stroke="none" fill="blue" font-size="40" font-family="ZT Nature">12</text>

</g></defs> <g class="etdrop" stroke="#777" stroke-width="2" stroke-linejoin="round" transform="translate(240,240) scale(1,1)" id="etanidrop"><g class="etboard">

<circle r="172" cx="0" cy="0" fill="#decfaa" stroke="purple" stroke-width="2" class="bcenter0"/>

<circle r="6" cx="-72" cy="-124.708" fill="purple" stroke="none" stroke-width="0" class="bcenter"/><circle r="6" cx="-124.708" cy="-72" fill="purple" stroke="none" stroke-width="0" class="bcenter"/><circle r="6" cx="-144" cy="0" fill="purple" stroke="none" stroke-width="0" class="bcenter"/><circle r="6" cx="-124.708" cy="72" fill="purple" stroke="none" stroke-width="0" class="bcenter"/><circle r="6" cx="-72" cy="124.708" fill="purple" stroke="none" stroke-width="0" class="bcenter"/><circle r="6" cx="0" cy="144" fill="purple" stroke="none" stroke-width="0" class="bcenter"/><circle r="6" cx="72" cy="124.708" fill="purple" stroke="none" stroke-width="0" class="bcenter"/><circle r="6" cx="124.708" cy="72" fill="purple" stroke="none" stroke-width="0" class="bcenter"/><circle r="6" cx="124.708" cy="-72" fill="purple" stroke="none" stroke-width="0" class="bcenter"/><circle r="6" cx="144" cy="0" fill="purple" stroke="none" stroke-width="0" class="bcenter"/><circle r="6" cx="72" cy="-124.708" fill="purple" stroke="none" stroke-width="0" class="bcenter"/><circle r="6" cx="0" cy="-144" fill="purple" stroke="none" stroke-width="0" class="bcenter"/>

<circle r="180" cx="0" cy="0" fill="none" stroke="purple" stroke-width="7" class="bcenter0"/>

 <style>
@font-face {
    font-family: 'ZT Nature';
    src: url('data:font/woff2;charset=utf-8;base64,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') format('woff2');
    font-weight: normal;
    font-style: normal;
    font-display: swap;
}
</style>
</g><use href="#tile10" fill="#ca5326" stroke="#777" transform="translate(-72,-124.708) scale(1,1) rotate(0)" class="tiledropped"/><use href="#tile11" fill="#ca5326" stroke="#777" transform="translate(0,-144) scale(1,1) rotate(0)" class="tiledropped"/><use href="#tile9" fill="#ca5326" stroke="#777" transform="translate(-124.708,-72) scale(1,1) rotate(0)" class="tiledropped"/><use href="#tile8" fill="#ca5326" stroke="#777" transform="translate(-144,0) scale(1,1) rotate(0)" class="tiledropped"/><use href="#tile7" fill="#ca5326" stroke="#777" transform="translate(-124.708,72) scale(1,1) rotate(0)" class="tiledropped"/><use href="#tile6" fill="#ca5326" stroke="#777" transform="translate(-72,124.708) scale(1,1) rotate(0)" class="tiledropped"/><use href="#tile4" fill="#ca5326" stroke="#777" transform="translate(72,124.708) scale(1,1) rotate(0)" class="tiledropped"/><use href="#tile2" fill="#ca5326" stroke="#777" transform="translate(144,0) scale(1,1) rotate(0)" class="tiledropped"/><use href="#tile3" fill="#ca5326" stroke="#777" transform="translate(124.708,72) scale(1,1) rotate(0)" class="tiledropped"/><use href="#tile1" fill="#ca5326" transform="translate(124.708,-72) scale(1,1) rotate(0)" class="tiledropped"/><use href="#tile0" fill="#ca5326" stroke="#777" transform="translate(72,-124.708) scale(1,1) rotate(0)" class="tiledropped"/><use href="#tile5" fill="#ca5326" stroke="#777" transform="translate(0,144) scale(1,1) rotate(0)" class="tilesnapped tiledropped"/></g></svg>

Re: 將改進混亂時鐘的網頁版

发表于 : 2026年 2月 1日 19:24
ejsoon
寫一段js代碼:

一,頁面上可能有一個或多個object.cclok_main元素,首先js探測其是否有至少一個,如果沒有則停止,有則往下執行。

二,每一個object.cclok_main中有屬性ckmode,它的值可能是"button"或"number"。

三,拿到object中的svg中的g.etdrop中的所有<use>,這些use都有href="#tile0-11",一共有12個,它們都有transform-translate,按href的數字把它們的translate保存在一個數組translatearray中。

比如use.href="tile0"的translate保存在translatearray[0]中。

四,如果object.cclok_main的屬性ckmode的值為"button",則在其下方生成一個input.generatecclok[type=button]{random},。當點擊它時,這些use的translate將會隨機更改,但不能是原來的translate。

五,如果object.cclok_main的屬性ckmode的值為"number",則它還會有一個屬性ckvalue,把ckvalue的值用「,」分隔,之後把每一個數字減1後,調取對應的use.href="tile",再按translatearray的次序重寫use的translate。

例如ckvalue="7,6,10,8,4,11,9,12,5,3,1,2",則use.href="tile6"的translate改成translatearray[0],use.href="tile5"的translate改成translatearray[1],use.href="tile9"的translate改成translatearray[2]。

代碼全部要使用英文。

代码: 全选

以下是一段符合你描述的 JavaScript 代碼(純原生,不依賴任何框架):

```javascript
(function() {
    // 找出所有 .cclok_main 的 object 元素
    const containers = document.querySelectorAll('object.cclok_main');
    
    // 如果一個都沒有,就直接結束
    if (containers.length === 0) {
        return;
    }

    containers.forEach(object => {
        const mode = object.getAttribute('ckmode');
        if (!mode) return;

        // 找到 object 內部的 SVG 文件中的內容
        // 注意:object 載入的 SVG 必須已經完成載入才能正確取得內容
        const svgDoc = object.contentDocument || object.getSVGDocument();
        if (!svgDoc) return;

        const g = svgDoc.querySelector('g.etdrop');
        if (!g) return;

        const uses = g.querySelectorAll('use[href^="#tile"]');
        if (uses.length !== 12) return;

        // 儲存原始的 translate 位置,按 tile0~tile11 的順序
        const translateArray = new Array(12);

        uses.forEach(use => {
            const href = use.getAttribute('href');           // #tile0 ~ #tile11
            const match = href.match(/#tile(\d+)/);
            if (!match) return;

            const index = parseInt(match[1], 10);
            if (index < 0 || index > 11) return;

            const transform = use.getAttribute('transform') || '';
            translateArray[index] = transform;
        });

        // ------------------------------
        // 模式 1:button 模式 - 隨機打亂(不重複原位置)
        // ------------------------------
        if (mode === 'button') {
            const btn = document.createElement('input');
            btn.type = 'button';
            btn.value = 'random';
            btn.className = 'generatecclok';
            
            // 插入按鈕 - 放在 object 後面
            object.parentNode.insertBefore(btn, object.nextSibling);

            btn.addEventListener('click', () => {
                // 產生 0~11 的亂序排列,且不能跟原順序一樣
                let shuffled = [...Array(12).keys()]; // 0~11
                
                let isSameAsOriginal = true;
                let attempts = 0;
                const maxAttempts = 100;

                while (isSameAsOriginal && attempts < maxAttempts) {
                    shuffled.sort(() => Math.random() - 0.5);
                    isSameAsOriginal = shuffled.every((v, i) => v === i);
                    attempts++;
                }

                // 套用新的位置
                uses.forEach(use => {
                    const href = use.getAttribute('href');
                    const match = href.match(/#tile(\d+)/);
                    if (!match) return;
                    
                    const originalIndex = parseInt(match[1], 10);
                    const newIndex = shuffled[originalIndex];
                    
                    use.setAttribute('transform', translateArray[newIndex]);
                });
            });
        }

        // ------------------------------
        // 模式 2:number 模式 - 按照 ckvalue 指定的順序套用
        // ------------------------------
        else if (mode === 'number') {
            const ckvalue = object.getAttribute('ckvalue');
            if (!ckvalue) return;

            const orderStr = ckvalue.split(',').map(s => s.trim());
            if (orderStr.length !== 12) return;

            const targetIndices = orderStr.map(n => {
                const num = parseInt(n, 10);
                return isNaN(num) ? -1 : num - 1;   // 題目說要減1
            });

            if (targetIndices.some(n => n < 0 || n > 11)) return;

            // 按順序套用 translateArray 中的位置
            uses.forEach(use => {
                const href = use.getAttribute('href');
                const match = href.match(/#tile(\d+)/);
                if (!match) return;

                const tileNum = parseInt(match[1], 10);           // 0~11
                const positionIndex = targetIndices.indexOf(tileNum); // 這個 tile 應該出現在第幾個位置

                if (positionIndex === -1) return;

                const newTransform = translateArray[positionIndex];
                if (newTransform) {
                    use.setAttribute('transform', newTransform);
                }
            });
        }
    });
})();
```

### 重要說明:

1. 程式使用立即執行函式,避免污染全域
2. 假設 `<object class="cclok_main">` 已經載入 SVG 內容(大多數情況下需要等待 load 事件)
3. 如果你的 object 是動態插入的,建議改用 MutationObserver 或在適當的時機再執行這段程式
4. button 模式使用簡單的 Fisher-Yates 亂序 + 避免完全相同原順序的處理
5. number 模式會把 ckvalue 的數字視為「tile編號」(從1開始),減1後當成索引來找對應的原始 translate

需要更穩定的做法(等待 object 載入完成)可以改成:

```javascript
document.querySelectorAll('object.cclok_main').forEach(obj => {
    obj.addEventListener('load', () => {
        // 在這裡放上面的邏輯
    });
});
```

希望對你有幫助!

Re: 將改進混亂時鐘的網頁版

发表于 : 2026年 2月 1日 19:26
ejsoon
gemini3

代码: 全选

document.addEventListener("DOMContentLoaded", function() {
    // 1. Detect if object.cclok_main exists. If not, stop.
    const mainObjects = document.querySelectorAll("object.cclok_main");
    if (mainObjects.length === 0) {
        return;
    }

    mainObjects.forEach(function(obj) {
        // Function to initialize the logic for each object
        const initClock = function() {
            // Determine the context (handle if it's an <object> tag or a standard div)
            const doc = obj.contentDocument || obj; 
            
            // Find the svg and g.etdrop
            // Note: If 'obj' is a div containing inline SVG, we query inside it. 
            // If 'obj' is an <object> tag, we query its contentDocument.
            const dropGroup = doc.querySelector("svg g.etdrop");
            
            if (!dropGroup) return;

            // 3. Extract <use> elements and their translations
            const translateArray = [];
            const tileElements = [];

            // We need to capture tiles from tile0 to tile11 specifically
            for (let i = 0; i < 12; i++) {
                // Find element by href="#tileX"
                const tile = dropGroup.querySelector(`use[href="#tile${i}"]`);
                
                if (tile) {
                    tileElements[i] = tile; // Store the DOM element
                    // Extract the transform string (e.g., "translate(10, 20)")
                    translateArray[i] = tile.getAttribute("transform");
                }
            }

            // Check attribute ckmode
            const ckmode = obj.getAttribute("ckmode");

            // 4. If ckmode is "button"
            if (ckmode === "button") {
                // Create input button
                const btn = document.createElement("input");
                btn.type = "button";
                btn.className = "generatecclok";
                btn.value = "random";

                // Insert button after the object
                obj.parentNode.insertBefore(btn, obj.nextSibling);

                // Add click event
                btn.addEventListener("click", function() {
                    // Create a copy of positions to shuffle
                    let shuffledPositions = [...translateArray];

                    // Fisher-Yates shuffle algorithm
                    for (let i = shuffledPositions.length - 1; i > 0; i--) {
                        const j = Math.floor(Math.random() * (i + 1));
                        [shuffledPositions[i], shuffledPositions[j]] = [shuffledPositions[j], shuffledPositions[i]];
                    }

                    // Apply new random positions to the tiles
                    tileElements.forEach((tile, index) => {
                        if (tile) {
                            tile.setAttribute("transform", shuffledPositions[index]);
                        }
                    });
                });
            }

            // 5. If ckmode is "number"
            else if (ckmode === "number") {
                const ckvalue = obj.getAttribute("ckvalue");
                
                if (ckvalue) {
                    // Split string by comma and convert to integers
                    const values = ckvalue.split(",");
                    
                    // Process the values
                    // logic: ckvalue item (minus 1) determines which TILE goes to the CURRENT position index
                    values.forEach((val, positionIndex) => {
                        const tileIndex = parseInt(val.trim()) - 1; // Convert "7" to index 6
                        
                        if (tileElements[tileIndex] && translateArray[positionIndex]) {
                            // Example: If 1st number is 7 (tileIndex 6), 
                            // we set tile6's transform to translateArray[0]
                            tileElements[tileIndex].setAttribute("transform", translateArray[positionIndex]);
                        }
                    });
                }
            }
        };

        // Handle loading logic in case the object is an external SVG file
        if (obj.tagName.toLowerCase() === 'object') {
            if (obj.contentDocument && obj.contentDocument.readyState === 'complete') {
                initClock();
            } else {
                obj.addEventListener('load', initClock);
            }
        } else {
            // If it is a div or inline svg
            initClock();
        }
    });
});

Re: 將改進混亂時鐘的網頁版

发表于 : 2026年 2月 1日 20:40
ejsoon
如果存在多個object,它們之間應該是獨立的,互不影響。但是你給的代碼卻會相互影響,請找出問題並修正。

gemini3:
這是一個修正後的版本。

為了確保多個 object 之間絕對獨立(互不影響),我將代碼邏輯封裝在一個獨立的函數 initSingleClock 中。這樣,每一個時鐘都有自己獨立的變量作用域(Scope),變量如 translateArray 和 tileElements 也是彼此隔離的,不會發生數據污染。

此外,我修正了「隨機排序但不能是原來的」的邏輯,確保隨機生成的結果不會剛好和初始狀態一模一樣。

代码: 全选

document.addEventListener("DOMContentLoaded", function() {
    // 1. Detect if object.cclok_main exists.
    const mainObjects = document.querySelectorAll("object.cclok_main");
    if (mainObjects.length === 0) {
        return;
    }

    // Process each object independently
    mainObjects.forEach(function(obj) {
        // Check if the object is already loaded, otherwise wait for load event
        // This handles both inline SVG (divs) and external SVG (<object>)
        if (obj.tagName.toLowerCase() === 'object' && !obj.contentDocument) {
            obj.addEventListener('load', function() {
                initSingleClock(obj);
            });
        } else {
            // Already loaded or is a div wrapper
            initSingleClock(obj);
        }
    });

    /**
     * Encapsulates all logic for a single clock instance.
     * This guarantees that variables are not shared between different clocks.
     */
    function initSingleClock(obj) {
        // Determine the context (internal document for <object> or current doc for <div>)
        const doc = (obj.contentDocument) ? obj.contentDocument : obj;

        // 2. Find the specific group inside THIS object
        // We use querySelector on 'doc' to restrict search to this clock only
        const dropGroup = doc.querySelector("g.etdrop");
        
        if (!dropGroup) return; // Stop if structure is missing

        // 3. Extract <use> elements and their translations
        // These arrays are local to this function scope (Independent for each clock)
        const translateArray = []; // Stores the original translate strings
        const tileElements = [];   // Stores the DOM elements

        // Loop 0 to 11 to find tiles specifically by their href ID
        for (let i = 0; i < 12; i++) {
            // Support both standard href and deprecated xlink:href just in case
            let tile = dropGroup.querySelector(`use[href="#tile${i}"]`) || 
                       dropGroup.querySelector(`use[xlink\\:href="#tile${i}"]`);

            if (tile) {
                tileElements[i] = tile;
                // Save the initial transform (e.g., "translate(100, 200)")
                translateArray[i] = tile.getAttribute("transform");
            }
        }

        // Get the mode
        const ckmode = obj.getAttribute("ckmode");

        // 4. Mode: BUTTON
        if (ckmode === "button") {
            const btn = document.createElement("input");
            btn.type = "button";
            btn.className = "generatecclok";
            btn.value = "random";

            // Insert button strictly after the current object
            obj.parentNode.insertBefore(btn, obj.nextSibling);

            btn.addEventListener("click", function() {
                let shuffledPositions;
                let isSameAsOriginal;

                // Loop ensures we don't accidentally generate the exact original sequence
                do {
                    // Create a copy to shuffle
                    shuffledPositions = [...translateArray];
                    
                    // Fisher-Yates Shuffle
                    for (let i = shuffledPositions.length - 1; i > 0; i--) {
                        const j = Math.floor(Math.random() * (i + 1));
                        [shuffledPositions[i], shuffledPositions[j]] = [shuffledPositions[j], shuffledPositions[i]];
                    }

                    // Check if it matches original exactly
                    isSameAsOriginal = shuffledPositions.every((val, index) => val === translateArray[index]);

                } while (isSameAsOriginal);

                // Apply new positions to the tiles of THIS clock
                tileElements.forEach((tile, index) => {
                    if (tile) {
                        tile.setAttribute("transform", shuffledPositions[index]);
                    }
                });
            });
        }

        // 5. Mode: NUMBER
        else if (ckmode === "number") {
            const ckvalue = obj.getAttribute("ckvalue");
            
            if (ckvalue) {
                const values = ckvalue.split(",");
                
                // Logic: 
                // ckvalue = "7, ..." -> The 1st position (index 0) should be occupied by Tile 6 (7-1).
                // So we find Tile 6, and give it the transform of Position 0.
                values.forEach((val, positionIndex) => {
                    // Convert "7" to index 6
                    const tileIndex = parseInt(val.trim()) - 1; 
                    
                    if (tileElements[tileIndex] && translateArray[positionIndex]) {
                        tileElements[tileIndex].setAttribute("transform", translateArray[positionIndex]);
                    }
                });
            }
        }
    }
});
主要修正點:

獨立作用域 (Independent Scope): 我創建了一個 initSingleClock(obj) 函數。所有的變量(translateArray, tileElements)都在這個函數內部聲明。這意味著每個 object 都有自己的一套數據,絕對不會因為點擊了時鐘 A 的按鈕而影響時鐘 B。

更嚴謹的選擇器 (Scoped Selectors): 使用了 doc.querySelector 而不是全局搜索,確保 JS 只抓取當前 object 內部的 SVG 元素。

加載狀態檢查: 針對 <object> 標籤增加了 load 事件監聽,防止因為 SVG 尚未加載完成而導致腳本找不到元素。

隨機不重複邏輯: 在 button 模式中增加了一個 do...while 循環,確保隨機後的結果如果不幸和原始排列完全一樣,會重新隨機一次,直到不同為止。

兼容性: 增加了對 xlink:href 的查找支持(有些舊版 SVG 導出工具會使用 xlink),提高了代碼的健壯性。