NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Animation // @namespace https://greasyfork.org/users/281093 // @match https://sketchful.io/ // @grant none // @version 0.2 // @author Bell // @license MIT // @copyright 2020, Bell (https://openuserjs.org/users/Bell) // @require https://gitcdn.link/repo/antimatter15/jsgif/master/LZWEncoder.js // @require https://gitcdn.link/repo/antimatter15/jsgif/master/NeuQuant.js // @require https://gitcdn.link/repo/antimatter15/jsgif/master/GIFEncoder.js // @description 8/4/2020, 12:03:46 PM // ==/UserScript== /* jshint esversion: 6 */ /* eslint-disable no-undef */ 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 }', ]; const sheet = window.document.styleSheets[window.document.styleSheets.length - 1]; const outerContainer = document.createElement('div'); const canvasContainer = document.querySelector('#gameCanvas'); const canvasInner = document.querySelector("#gameCanvasInner"); 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: 10px; overflow-y: hidden; border-radius: 10px; margin-bottom: 5px; margin-left: 5vw; width: 100%`; const canvas = document.querySelector('#canvas'); const ctx = canvas.getContext('2d'); const encoder = new GIFEncoder(); const layers = []; const contextLayers = []; (() => { addLayerContainer(); styleRules.forEach((rule) => sheet.insertRule(rule)); document.addEventListener('keydown', handleInput); const gameModeObserver = new MutationObserver(checkRoomType); gameModeObserver.observe(document.querySelector('.game'), { attributes: true }); gameModeObserver.observe(canvas, { attributes: true }); })(); function checkRoomType() { outerContainer.style.display = isFreeDraw() ? 'flex' : 'none'; } function handleInput(e) { switch (e.code) { case 'KeyL': addLayer(); break; case 'KeyR': if (!e.shiftKey) return; const name = "sketchful-gif-" + Date.now(); render(name, 1000); break; } } function addLayer() { resetActiveLayer(); const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); contextLayers.push(copyCtx(imgData)); saveLayer(); const canvasLayer = createLayer(); const layerCtx = canvasLayer.getContext('2d'); layerCtx.putImageData(imgData, 0, 0); makeTransparent(layerCtx); // document.querySelector('#gameToolsClear').click(); const previousLayer = canvasInner.querySelector("#canvasLayer"); if (previousLayer) previousLayer.remove(); canvas.parentElement.insertBefore(canvasLayer, canvas); } function render(name, delay) { if (!contextLayers.length) return; encoder.setRepeat(0); encoder.setDelay(delay); encoder.start(); contextLayers.forEach(layer => { encoder.addFrame(layer); }); encoder.finish(); encoder.download(name + ".gif"); } function copyCtx(imgData) { const tempCanvas = document.createElement('canvas'); tempCanvas.width = canvas.width; tempCanvas.height = canvas.height; const tempCtx = tempCanvas.getContext('2d'); tempCtx.putImageData(imgData, 0, 0); return tempCtx; } function saveLayer() { const container = document.querySelector("#layerContainer"); // const div = document.createElement("div"); const img = document.createElement("img"); img.style.width = "133px"; img.style.cursor = "pointer"; img.style.marginRight = "5px"; img.src = canvas.toDataURL(); layers.push(img.src); container.append(img); } function setActiveLayer(e) { const img = e.target; if (img.tagName !== "IMG") return; resetActiveLayer(); const canvasLayerCtx = document.querySelector("#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); } img.id = "activeLayer"; img.style.border = "3px solid red"; ctx.drawImage(img, 0, 0); } function resetActiveLayer() { const layer = document.querySelector("#activeLayer"); if (!layer) return; layer.id = ""; layer.style.border = ""; } function createLayer() { const canvasLayer = document.createElement('canvas'); canvasLayer.style.width = '100%'; canvasLayer.style.position = 'absolute'; canvasLayer.style.pointerEvents = 'none'; canvasLayer.style.imageRendering = 'pixelated'; canvasLayer.style.filter = 'opacity(0.5)'; canvasLayer.width = canvas.width; canvasLayer.height = canvas.height; canvasLayer.id = "canvasLayer"; return canvasLayer; } function downloadGif(data, name) { let a = document.createElement("a"); a.download = name + ".gif"; a.href = data; a.click(); } function addButton(text, clickFunction, element, type) { const button = document.createElement("div"); button.setAttribute("class", `btn btn-${type}`); button.setAttribute("style", "height: fit-content; margin-top: 10px; margin-left: 10px;"); button.textContent = text; button.onclick = clickFunction; element.append(button); } function getInterval() { let interval = parseInt(document.querySelector("#gifInterval").value); if (isNaN(interval) || interval < 0 || interval > 5000) interval = 100; return interval; } function renderGif() { const interval = getInterval(); const name = "sketchful-gif-" + Date.now(); render(name, interval); console.log("rendered " + name); } function removeLayer() { const activeLayer = document.querySelector('#activeLayer'); const layerContainer = document.querySelector('#layerContainer'); if (!activeLayer) return; const index = Array.prototype.indexOf.call(layerContainer.children, activeLayer); contextLayers.splice(index, 1); activeLayer.remove(); } 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"; container.setAttribute("style", containerStyle); container.setAttribute("ondragstart", "return false"); outerContainer.append(container); const buttonContainer = document.createElement("div"); buttonContainer.style.width = "15%"; outerContainer.append(buttonContainer); addButton("Save Gif", renderGif, buttonContainer, "warning"); addButton("Save Layer", addLayer, buttonContainer, "info"); addButton("Delete Layer", removeLayer, buttonContainer, "danger"); addButton("Play", playAnimation, buttonContainer, "success"); const textInput = document.createElement("input"); textInput.style.width = "100px"; textInput.placeholder = "Interval (ms)"; textInput.style.marginTop = "10px"; textInput.style.marginLeft = "10px"; textInput.id = "gifInterval"; setInputFilter(textInput, function(value) { return /^\d*\.?\d*$/.test(value); }); buttonContainer.append(textInput); game.append(outerContainer); } let animating = null; function playAnimation(e) { const playButton = e.target; const canvasLayer = document.querySelector("#canvasLayer"); if (playButton.textContent === "Stop") { playButton.classList.toggle("btn-success"); playButton.classList.toggle("btn-danger"); playButton.textContent = "Play"; if (animating) clearInterval(animating); const preview = document.querySelector("#gifPreview"); if (preview) preview.remove(); if (canvasLayer) canvasLayer.style.display = ""; return; } if (canvasLayer) canvasLayer.style.display = "none"; const canvasCover = document.querySelector("#canvasCover"); const layerContainer = document.querySelector("#layerContainer"); const img = document.createElement('img'); img.style.position = "absolute"; img.style.width = "100%"; img.style.imageRendering = "pixelated"; img.id = "gifPreview"; canvasCover.parentElement.insertBefore(img, canvasCover); let frame = layerContainer.firstChild; if (!frame) return; const interval = getInterval(); playButton.classList.toggle("btn-success"); playButton.classList.toggle("btn-danger"); playButton.textContent = "Stop"; animating = setInterval(() => { img.src = frame.src; frame = frame.nextSibling || layerContainer.firstChild; }, 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 == 255 && g == 255 && b == 255) { data[i + 3] = 0; } } context.putImageData(imgData, 0, 0); }