您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Animation tools for Sketchful.io
当前为
// ==UserScript== // @name Animation // @namespace https://greasyfork.org/users/281093 // @match https://sketchful.io/ // @grant none // @version 0.6.6 // @author Bell // @license MIT // @copyright 2020, Bell (https://openuserjs.org/users/Bell) // @require https://cdnjs.cloudflare.com/ajax/libs/gifshot/0.3.2/gifshot.min.js // @require https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/libgif.min.js // @description Animation tools for Sketchful.io // ==/UserScript== /* jshint esversion: 6 */ const containerStyle = `white-space: nowrap; overflow: auto; justify-content:center; margin-top: 10px; max-width: 76%; height: 124px; background: rgb(0 0 0 / 30%); padding: 12px; overflow-y: hidden; border-radius: 10px; margin-bottom: 5px; margin-left: 5vw; width: 100%; user-select: none;`; const canvasLayerStyle = `width: 100%; position: absolute; pointer-events: none; image-rendering: pixelated; filter: opacity(0.5);`; const styleRules = [ '#layerContainer::-webkit-scrollbar { width: 5px; height: 5px; overflow: hidden}', '#layerContainer::-webkit-scrollbar-track { background: none }', '#layerContainer::-webkit-scrollbar-thumb { background: #F5BC09; border-radius: 5px }', `#layerContainer { ${containerStyle} }`, `.layer { ${canvasLayerStyle} }`, '#layerContainer img { width: 133px; cursor: pointer; margin-right: 5px }', '#buttonContainer { max-width: 230px; min-width: 230px; }', '#buttonContainer div { height: fit-content; margin-top: 10px; margin-left: 10px; }', '#buttonContainer { width: 15%; padding-top: 5px }', '#gifPreview { position: absolute; z-index: 1; width: 100%; image-rendering: pixelated; }', '.hidden { display: none }', '#activeLayer { margin-top: -1px; border: 3px solid red }' ]; const sheet = window.document.styleSheets[window.document.styleSheets.length - 1]; const outerContainer = document.createElement('div'); const onionContainer = document.createElement('div'); const canvasContainer = document.querySelector('#gameCanvas'); const canvasInner = document.querySelector("#gameCanvasInner"); const canvas = document.querySelector('#canvas'); const ctx = canvas.getContext('2d'); const layerContainer = addLayerContainer(); let copied = null; (() => { canvas.parentElement.insertBefore(onionContainer, canvas); addButtons(); styleRules.forEach((rule) => sheet.insertRule(rule)); const gameModeObserver = new MutationObserver(checkRoomType); gameModeObserver.observe(document.querySelector('.game'), { attributes: true }); gameModeObserver.observe(canvas, { attributes: true }); layerContainer.addEventListener("dragenter", highlight, false); layerContainer.addEventListener("dragleave", unhighlight, false); layerContainer.addEventListener("drop", handleDrop, false); layerContainer.addEventListener("dragover", function(event) { event.preventDefault(); }, false); document.addEventListener('keydown', (e) => { if (!e.ctrlKey || document.activeElement.tagName === "INPUT") return; const selectedLayer = document.querySelector("#activeLayer"); if (e.code === "KeyC") { if (!selectedLayer) return; copied = selectedLayer.cloneNode(); e.stopImmediatePropagation(); } else if (e.code === "KeyV" && copied) { const copy = copied.cloneNode(); if (selectedLayer) insertAfter(copy, selectedLayer); else layerContainer.append(copy); resetActiveLayer(); setActiveLayer({ target: copy }); } }); })(); function checkRoomType() { outerContainer.style.display = isFreeDraw() ? 'flex' : 'none'; onionContainer.style.display = isFreeDraw() ? "" : "none"; } function addLayer() { const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); saveLayer(canvas); const canvasLayer = document.querySelector("#canvasLayer") || createLayer(); const layerCtx = canvasLayer.getContext('2d'); layerCtx.putImageData(imgData, 0, 0); makeTransparent(layerCtx); const previousLayer = canvasInner.querySelector("#canvasLayer"); if (previousLayer) previousLayer.remove(); onionContainer.appendChild(canvasLayer); } function saveGif() { const container = document.querySelector("#layerContainer"); if (!container.childElementCount) return; const layers = Array.from(container.children).map(image => image.src); const interval = getInterval(); gifshot.createGIF({ gifWidth: canvas.width, gifHeight: canvas.height, interval: interval / 1000, images: layers }, downloadGif); } function extractFrames(img) { const gifLoaderTemp = document.createElement('div'); gifLoaderTemp.style.display = "none"; gifLoaderTemp.append(img); document.body.append(gifLoaderTemp); img.setAttribute ("rel:auto_play", 0); const gif = new SuperGif({ gif: img }); gif.load(()=> { const gifCanvas = gif.get_canvas(); if (gifCanvas.width !== canvas.width || gifCanvas.height !== canvas.height) { alert("Not a sketchful gif"); return; } if (!document.querySelector("#canvasLayer")) onionContainer.appendChild(createLayer()); const numFrames = gif.get_length(); for (let i = 0; i < numFrames; i++) { gif.move_to(i); saveLayer(gifCanvas); } }); } function handleDrop(e) { e.preventDefault(); layerContainer.style.filter = ""; let dt = e.dataTransfer; let files = dt.files; if (files.length && files !== null) { handleFiles(files); } } function handleFiles(files) { files = [...files]; files.forEach(previewFile); } function previewFile(file) { let reader = new FileReader(); reader.readAsDataURL(file); reader.onloadend = function () { let gif = document.createElement('img'); gif.src = reader.result; extractFrames(gif); }; } function highlight(e) { e.preventDefault(); layerContainer.style.filter = "drop-shadow(0px 0px 6px green)"; } function unhighlight(e) { e.preventDefault(); layerContainer.style.filter = ""; } function saveLayer(canv) { const activeLayer = document.querySelector("#activeLayer"); const container = document.querySelector("#layerContainer"); const img = document.createElement("img"); img.src = canv.toDataURL(); if (activeLayer) { insertAfter(img, activeLayer); setActiveLayer({ target: img }); } else { container.append(img); } } function insertAfter(newNode, referenceNode) { referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } function setActiveLayer(e) { const img = e.target; if (img.tagName !== "IMG") { resetActiveLayer(); return; } resetActiveLayer(); img.id = "activeLayer"; ctx.drawImage(img, 0, 0); const canvasLayer = document.querySelector("#canvasLayer"); if (!canvasLayer) return; const canvasLayerCtx = canvasLayer.getContext("2d"); const previousImg = img.previousSibling; if (previousImg) { canvasLayerCtx.drawImage(previousImg, 0, 0); makeTransparent(canvasLayerCtx); } else { canvasLayerCtx.clearRect(0, 0, canvas.width, canvas.height); } } function resetActiveLayer() { const layer = document.querySelector("#activeLayer"); if (!layer) return; layer.id = ""; layer.style.border = ""; } function createLayer() { const canvasLayer = document.createElement('canvas'); canvasLayer.classList.add("layer"); canvasLayer.width = canvas.width; canvasLayer.height = canvas.height; canvasLayer.id = "canvasLayer"; return canvasLayer; } function downloadGif(obj) { const name = "sketchful-gif-" + Date.now(); let a = document.createElement("a"); a.download = name + ".gif"; a.href = obj.image; a.click(); } function addButton(text, clickFunction, element, type) { const button = document.createElement("div"); button.setAttribute("class", `btn btn-sm btn-${type}`); button.textContent = text; button.onpointerup = clickFunction; element.append(button); return button; } function clamp(num, min, max) { return num <= min ? min : num >= max ? max : num; } function getInterval() { const input = document.querySelector("#gifIntervalInput"); let interval = parseInt(input.value); if (isNaN(interval)) interval = 100; interval = clamp(interval, 20, 10000); input.value = interval; return interval; } function removeLayer() { const activeLayer = document.querySelector('#activeLayer'); const layerContainer = document.querySelector('#layerContainer'); if (!activeLayer) return; const index = nodeIndex(activeLayer); activeLayer.remove(); } function nodeIndex(node) { return Array.prototype.indexOf.call(node.parentNode.children, node); } function addButtons() { const buttonContainer = document.createElement("div"); buttonContainer.id = "buttonContainer"; outerContainer.append(buttonContainer); addButton("Save Gif", saveGif, buttonContainer, "warning"); addButton("Onion", toggleOnion, buttonContainer, "success"); addButton("Save Layer", addLayer, buttonContainer, "info"); addButton("Delete Layer", removeLayer, buttonContainer, "danger"); addButton("Play", playAnimation, buttonContainer, "success"); const textDiv = document.createElement('div'); const textInput = document.createElement("input"); textDiv.classList.add('btn'); textDiv.style.padding = '0px'; textInput.placeholder = "Interval (ms)"; textInput.style.width = "100px"; textInput.id = "gifIntervalInput"; setInputFilter(textInput, (v) => {return /^\d*\.?\d*$/.test(v);}); textDiv.append(textInput); buttonContainer.append(textDiv); } function addLayerContainer() { const game = document.querySelector("body > div.game"); const container = document.createElement("div"); outerContainer.style.display = "flex"; outerContainer.style.flexDirection = "row"; container.addEventListener('wheel', (e) => { if (e.deltaY > 0) container.scrollLeft += 100; else container.scrollLeft -= 100; e.preventDefault(); }); container.addEventListener('pointerdown', setActiveLayer, true); container.id = "layerContainer"; new Sortable(container, { animation: 150, }); outerContainer.append(container); game.append(outerContainer); return container; } let onion = true; function toggleOnion(e) { onion = !onion; this.textContent = onion ? "Onion" : "Onioff"; onionContainer.classList.toggle("hidden"); this.classList.toggle("btn-success"); this.classList.toggle("btn-danger"); } let animating = false; function playAnimation(e) { const preview = document.querySelector("#gifPreview"); if (animating) { this.classList.toggle("btn-success"); this.classList.toggle("btn-danger"); this.textContent = "Play"; if (preview) preview.remove(); animating = false; return; } const canvasCover = document.querySelector("#canvasCover"); const layerContainer = document.querySelector("#layerContainer"); const img = document.createElement('img'); img.id = "gifPreview"; img.draggable = false; canvasCover.parentElement.insertBefore(img, canvasCover); let frame = layerContainer.firstChild; if (!frame) return; const interval = getInterval(); this.classList.toggle("btn-success"); this.classList.toggle("btn-danger"); this.textContent = "Stop"; animating = true; (function playFrame() { if (!animating) return; img.src = frame.src; frame = frame.nextSibling || layerContainer.firstChild; setTimeout(playFrame, interval); })(); } function isFreeDraw() { return ( document.querySelector("#canvas").style.display !== 'none' && document.querySelector('#gameClock').style.display === 'none' && document.querySelector('#gameSettings').style.display === 'none' ); } function setInputFilter(textbox, inputFilter) { ["input", "keydown", "keyup", "mousedown", "mouseup", "select", "contextmenu", "drop"].forEach(function(event) { textbox.addEventListener(event, function() { if (inputFilter(this.value)) { this.oldValue = this.value; this.oldSelectionStart = this.selectionStart; this.oldSelectionEnd = this.selectionEnd; } else if (this.hasOwnProperty("oldValue")) { this.value = this.oldValue; this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd); } else { this.value = ""; } }); }); } function makeTransparent(context) { const imgData = context.getImageData(0, 0, canvas.width, canvas.height); const data = imgData.data; for(let i = 0; i < data.length; i += 4) { const [r, g, b] = data.slice(i, i + 3); if (r >= 210 && g >= 210 && b >= 210) { data[i + 3] = 0; } } context.putImageData(imgData, 0, 0); }