您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Ctrl + Click and drag to draw a gradient, click a color to set it as the first and right click a color to set it as the second.
// ==UserScript== // @name Gradient Tool // @namespace https://greasyfork.org/users/281093 // @match https://sketchful.io/ // @grant none // @version 1.0 // @author Bell // @license MIT // @copyright 2020, Faux (https://greasyfork.org/users/281093) // @description Ctrl + Click and drag to draw a gradient, click a color to set it as the first and right click a color to set it as the second. // ==/UserScript== /* jshint esversion: 8 */ const canvas = document.querySelector('#canvas'); const colorsDiv = document.querySelector('#gameToolsColors'); const lineCanvas = document.createElement('canvas'); const lineCtx = lineCanvas.getContext('2d'); const sizeInput = document.querySelector("#gameToolsColorPreview"); lineCanvas.style.position = 'absolute'; lineCanvas.style.cursor = 'crosshair'; lineCanvas.style.width = '100%'; lineCanvas.style.display = 'none'; lineCanvas.style.userSelect = 'none'; lineCanvas.style.zIndex = '2'; lineCanvas.style.filter = 'opacity(0.7)'; lineCanvas.oncontextmenu = () => { return false; }; [lineCanvas.width, lineCanvas.height] = [canvas.width, canvas.height]; canvas.parentElement.insertBefore(lineCanvas, canvas); lineCanvas.clear = () => { lineCtx.clearRect(0, 0, lineCanvas.width, lineCanvas.height); }; canvas.save = () => { canvas.dispatchEvent(createMouseEvent('pointerup', { x: 0, y: 0 }, true)); }; let origin = {}; let realOrigin = {}; let previewPos = {}; let realPos = {}; let canvasHidden = true; let drawingLine = false; let activeColor = [0, 0, 0]; let color1 = [0, 200, 0]; let color2 = [250, 0, 0]; let drawingGradient = false; let queueKeyUp = false; document.addEventListener('keydown', (e) => { if (!isDrawing()) return; if (e.code === 'ControlLeft' && canvasHidden) { lineCanvas.style.display = ''; disableScroll(); canvasHidden = false; } }); document.addEventListener('keyup', (e) => { if (e.code === 'Digit1' && e.shiftKey) color1 = activeColor; else if (e.code === 'Digit2' && e.shiftKey) color2 = activeColor; if (e.code === 'ControlLeft' && !canvasHidden) { if (drawingGradient) { queueKeyUp = true; return; } onKeyUp(); } }); function onKeyUp() { lineCanvas.style.display = 'none'; canvasHidden = true; enableScroll(); resetLineCanvas(); document.removeEventListener('pointermove', savePos); document.removeEventListener('pointerup', pointerUpDraw); } function savePos(e) { previewPos = getPos(e); realPos = getRealPos(e); if (canvasHidden || !drawingLine) return; lineCanvas.clear(); drawPreviewLine(previewPos); e.preventDefault(); } colorsDiv.addEventListener('pointerdown', (e) => { if (!e.target.classList.contains('gameToolsColor') || e.button === 2) return; const colorStr = e.target.style.background; const match = colorStr.match(/rgb\((\d+),\s*(\d+),\s*(\d+)/); activeColor = match.slice(1, 4).map(str => parseInt(str)); color1 = activeColor; }); colorsDiv.addEventListener('contextmenu', (e) => { if (!e.target.classList.contains('gameToolsColor')) return; e.preventDefault(); const colorStr = e.target.style.background; const match = colorStr.match(/rgb\((\d+),\s*(\d+),\s*(\d+)/); color2 = match.slice(1, 4).map(str => parseInt(str)); }); lineCanvas.addEventListener('pointerdown', (e) => { if (drawingGradient) return; origin = getPos(e); realOrigin = getRealPos(e); drawingLine = true; document.addEventListener('pointerup', pointerUpDraw); document.addEventListener('pointermove', savePos); }); function pointerUpDraw(e) { document.removeEventListener('pointermove', savePos); document.removeEventListener('pointerup', pointerUpDraw); drawGradient(realOrigin.x, realOrigin.y, realPos.x, realPos.y); previewPos = getPos(e); realPos = getRealPos(e); resetLineCanvas(); } async function drawGradient(x1, y1, x2, y2) { drawingGradient = true; const lab1 = rgb2lab(color1); const lab2 = rgb2lab(color2); const scale = canvas.getBoundingClientRect().width / lineCanvas.width; const offset = (parseInt(sizeInput.value) / 2) * scale; const max = Math.round((y2 - y1) / offset) * 1.6; for (let s = 0; s <= max; s += 1) { const t = mapRange(s, 0, max, 0, 1); const y = lerp(y1 + offset, y2 - offset, t); const color = lerpColors(lab1, lab2, t); changeColor(lab2rgb(color)); drawLine(x1 + offset, y, x2 - offset, y); await delay(10); } canvas.save(); drawingGradient = false; if (queueKeyUp) { onKeyUp(); queueKeyUp = false; } } function mapRange(value, istart, istop, ostart, ostop) { return ostart + (ostop - ostart) * ((value - istart) / (istop - istart)); } function delay(time) { return new Promise((resolve) => { setTimeout(() => resolve(time), time); }); } function changeColor(rgbArray) { const colorElement = document.querySelector('.gameToolsColor'); const prevColor = colorElement.style.background; colorElement.style.background = `rgb(${rgbArray[0]}, ${rgbArray[1]}, ${rgbArray[2]})`; colorElement.dispatchEvent(new Event('pointerdown')); colorElement.style.background = prevColor; } function resetLineCanvas() { drawingLine = false; lineCanvas.clear(); } function getPos(event) { const canvasRect = canvas.getBoundingClientRect(); const canvasScale = canvas.width / canvasRect.width; return { x: (event.clientX - canvasRect.left) * canvasScale, y: (event.clientY - canvasRect.top) * canvasScale }; } function getRealPos(event) { return { x: event.clientX, y: event.clientY }; } function drawPreviewLine(pos) { const offset = parseInt(sizeInput.value) / 2; roundRect( lineCtx, origin.x, origin.y, pos.x - origin.x, pos.y - origin.y, offset, true, false, [ color1, color2 ] ); } function roundRect(ctx, x, y, width, height, radius, fill, stroke, gradientColors) { if (typeof stroke === 'undefined') { stroke = true; } if (typeof radius === 'undefined') { radius = 5; } if (typeof radius === 'number') { radius = {tl: radius, tr: radius, br: radius, bl: radius}; } else { var defaultRadius = {tl: 0, tr: 0, br: 0, bl: 0}; for (var side in defaultRadius) { radius[side] = radius[side] || defaultRadius[side]; } } ctx.beginPath(); ctx.moveTo(x + radius.tl, y); ctx.lineTo(x + width - radius.tr, y); ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr); ctx.lineTo(x + width, y + height - radius.br); ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height); ctx.lineTo(x + radius.bl, y + height); ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl); ctx.lineTo(x, y + radius.tl); ctx.quadraticCurveTo(x, y, x + radius.tl, y); ctx.closePath(); if (gradientColors) { const gradient = ctx.createLinearGradient(0, y, 0, height + y); const _color1 = `rgb(${gradientColors[0][0]}, ${gradientColors[0][1]}, ${gradientColors[0][2]}`; const _color2 = `rgb(${gradientColors[1][0]}, ${gradientColors[1][1]}, ${gradientColors[1][2]}`; gradient.addColorStop('0', _color1); gradient.addColorStop('1', _color2); ctx.strokeStyle = gradient; ctx.fillStyle = gradient; } if (fill) { ctx.fill(); } if (stroke) { ctx.lineWidth = 5; ctx.stroke(); } } function drawLine(x1, y1, x2, y2) { const coords = { x: x1, y: y1 }; const newCoords = { x: x2, y: y2 }; canvas.dispatchEvent(createMouseEvent('pointerdown', coords)); canvas.dispatchEvent(createMouseEvent('pointermove', newCoords)); } function createMouseEvent(name, pos, bubbles = false) { return new MouseEvent(name, { bubbles: bubbles, clientX: pos.x, clientY: pos.y, button: 0 }); } function lerp(a, b, t) { return a + (b - a) * t; } function lerpColors(lab1, lab2, t) { return [ lab1[0] + (lab2[0] - lab1[0]) * t, lab1[1] + (lab2[1] - lab1[1]) * t, lab1[2] + (lab2[2] - lab1[2]) * t, ]; } function rgb2lab(rgb) { let r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255, x, y, z; r = (r > 0.04045) ? ((r + 0.055) / 1.055) ** 2.4 : r / 12.92; g = (g > 0.04045) ? ((g + 0.055) / 1.055) ** 2.4 : g / 12.92; b = (b > 0.04045) ? ((b + 0.055) / 1.055) ** 2.4 : b / 12.92; x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047; y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 1.00000; z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883; x = (x > 0.008856) ? x ** (1 / 3) : (7.787 * x) + 16 / 116; y = (y > 0.008856) ? y ** (1 / 3) : (7.787 * y) + 16 / 116; z = (z > 0.008856) ? z ** (1 / 3) : (7.787 * z) + 16 / 116; return [(116 * y) - 16, 500 * (x - y), 200 * (y - z)]; } function lab2rgb(lab){ let y = (lab[0] + 16) / 116, x = lab[1] / 500 + y, z = y - lab[2] / 200, r, g, b; x = 0.95047 * ((x * x * x > 0.008856) ? x * x * x : (x - 16/116) / 7.787); y = 1.00000 * ((y * y * y > 0.008856) ? y * y * y : (y - 16/116) / 7.787); z = 1.08883 * ((z * z * z > 0.008856) ? z * z * z : (z - 16/116) / 7.787); r = x * 3.2406 + y * -1.5372 + z * -0.4986; g = x * -0.9689 + y * 1.8758 + z * 0.0415; b = x * 0.0557 + y * -0.2040 + z * 1.0570; r = (r > 0.0031308) ? (1.055 * Math.pow(r, 1/2.4) - 0.055) : 12.92 * r; g = (g > 0.0031308) ? (1.055 * Math.pow(g, 1/2.4) - 0.055) : 12.92 * g; b = (b > 0.0031308) ? (1.055 * Math.pow(b, 1/2.4) - 0.055) : 12.92 * b; return [ Math.max(0, Math.min(1, r)) * 255, Math.max(0, Math.min(1, g)) * 255, Math.max(0, Math.min(1, b)) * 255 ]; } const keys = { 32: 1, 37: 1, 38: 1, 39: 1, 40: 1 }; function preventDefault(e) { e.preventDefault(); } function preventDefaultForScrollKeys(e) { if (keys[e.keyCode]) { preventDefault(e); return false; } } function isDrawing() { return document.querySelector('#gameTools').style.display !== 'none' && document.querySelector('body > div.game').style.display !== 'none' && document.activeElement.tagName !== 'INPUT'; } let supportsPassive = false; try { window.addEventListener('test', null, Object.defineProperty({}, 'passive', { get: function() { supportsPassive = true; return true; } })); } catch(e) { console.log(e); } const wheelOpt = supportsPassive ? { passive: false } : false; const wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel' : 'mousewheel'; function disableScroll() { window.addEventListener('DOMMouseScroll', preventDefault, false); window.addEventListener(wheelEvent, preventDefault, wheelOpt); window.addEventListener('touchmove', preventDefault, wheelOpt); window.addEventListener('keydown', preventDefaultForScrollKeys, false); } function enableScroll() { window.removeEventListener('DOMMouseScroll', preventDefault, false); window.removeEventListener(wheelEvent, preventDefault, wheelOpt); window.removeEventListener('touchmove', preventDefault, wheelOpt); window.removeEventListener('keydown', preventDefaultForScrollKeys, false); }