將改進混亂時鐘的網頁版
发表于 : 2026年 1月 31日 03:15
mark
代码: 全选
<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>代码: 全选
以下是一段符合你描述的 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', () => {
// 在這裡放上面的邏輯
});
});
```
希望對你有幫助!代码: 全选
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();
}
});
});代码: 全选
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]);
}
});
}
}
}
});