您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A script that allows you to place templates over the canvas, it's easy to use and includes (almost) all fundamental features of a template tool.
// ==UserScript== // @name Pixeltracer -- PixelPlace Image Overlay Tool // @namespace https://pxphub.neocities.org/ // @version 0.20.0 // @description A script that allows you to place templates over the canvas, it's easy to use and includes (almost) all fundamental features of a template tool. // @author Realwdpcker // @license MIT // @match https://pixelplace.io/*-* // @icon data:image/png;base64,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 // @grant GM_addStyle // @grant GM_getResourceText // @require https://pixelplace.io/js/jquery.min.js?v2=1 // @require https://code.jquery.com/ui/1.14.1/jquery-ui.min.js // @require https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.js // @resource notyfCSS https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.css // ==/UserScript== /* * The icon used in this script is licensed under CC0 1.0 Universal (Public Domain). * See https://creativecommons.org/publicdomain/zero/1.0/ for more details. * * This script is distributed "as-is", without warranties. * * This code must not be sold, neither alone or as part of a bundle. If you paid for this script or received it as part of a bundle, please demand your money back immediately. * This code may also not be allowed to be processed through and/or modified with generative AI or generative tools AND/OR be modified without attribution to the original developer, if you so wish to, please contact the developer at @guildedbirdalt on Discord. */ (function() { 'use strict'; GM_addStyle(` @import url('https://fonts.googleapis.com/css2?family=Inconsolata&family=Lexend:[email protected]&family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap'); [material-icons] { font-family: 'Material Icons'; font-weight: normal; font-style: normal; font-size: 24px; display: inline-block; line-height: 1; letter-spacing: normal; text-transform: none; white-space: nowrap; direction: ltr; -webkit-font-feature-settings: 'liga'; -webkit-font-smoothing: antialiased; } .pt-ui { width: 200px; height: auto; background-color: #222222; z-index: 1000; display: none; position: absolute; top: 170px; left: 65px; padding: 5px; border-radius: 5px; border: #ffffff 2px solid; } .pt-settings-ui { width: auto; height: auto; background-color: #222222; z-index: 1000; display: none; position: absolute; top: 170px; left: 270px; padding: 5px; border-radius: 5px; border: #ffffff 2px solid; color: white; flex-direction: column; } .pt-ui input[type=range]::-webkit-slider-runnable-track { background-color: transparent; border-radius: 5px; border: 1px solid #444444; } .opacity-container input[type=range] { margin: 0px !important; } .pt-ui input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; background-color: white; border-radius: 5px; cursor: pointer; height: 10px; width: 10px; margin-top: 0.5px; transition: transform 0.1s ease-in-out; } .pt-ui input[type=range]::-webkit-slider-thumb:hover { transform: scale(1.75); } .x-header, .y-header, .scale-header, .opacity-header { font-size: 14px; color: white; font-family: Lexend, sans-serif; display: flex; align-items: center; column-gap: 2.5px; } .x-coordinate, .y-coordinate, .scale-input, .opacity-input { font-size: 14px; color: white; font-family: Mulish, sans-serif; } .x-coordinate, .y-coordinate, .scale-input { width: 70px; } .x-container, .y-container, .scale-container, .opacity-container { display: flex; column-gap: 2.5px; } .pt-label { font-size: 16px; color: white; font-family: Lexend, sans-serif; } .pt-version { font-size: 12px; color: #555555 !important; text-decoration: none; font-family: Lexend, sans-serif; } .pt-version:hover { color: #666666 !important; text-decoration: underline; } .hz-composite-divider { height: 2px; width: auto; z-index: 1001; position: relative; background: linear-gradient(90deg,rgba(83, 83, 83, 1) 0%, rgba(83, 83, 83, 1) 30%, rgba(83, 83, 83, 1) 70%, rgba(0, 0, 0, 0) 100%); margin: 3px 0px 3px 0px; } .vt-composite-divider { height: auto; width: 2px; z-index: 1001; position: relative; background: linear-gradient(180deg,rgba(0, 0, 0, 0) 0%, rgba(83, 83, 83, 1) 30%, rgba(83, 83, 83, 1) 70%, rgba(0, 0, 0, 0) 100%); margin: 0px 3px 0px 3px; } .header-container { display: flex; justify-content: space-between; } .action-buttons { display: flex; width: auto; gap: 5px; } .action-buttons > button { background-color: transparent !important; margin: 0px; padding: 0px; } .action-buttons > button:hover { background-color: transparent !important; filter: brightness(0.5); cursor: pointer; } .action-buttons > button:disabled, .action-buttons > button[disabled] { filter: none !important; pointer-events: none !important; cursor: not-allowed !important; opacity: 0.5; } .pt-overlay { position: absolute; top: 0; left: 0; pointer-events: none; z-index: 1000; } .tools-container { row-gap: 5px; display: flex; flex-direction: column; } .settings-btn, .minimize-ui-btn, .close-ui-btn { background-color: transparent !important; margin: 0px; padding: 0px; font-size: 14px; } .pt-settings-bg { display: none; width: 100vw; height: 100vh; background-color: #00000080; backdrop-filter: blur(2px); top: 0; position: absolute; } .file-reader { display: none; } .settings-container { display: flex; flex-direction: column; row-gap: 5px; font-size: 14px; color: white; font-family: Mulish, sans-serif; } .settings-container label, .hotkey-container label, .extra-container label, .hotkey-header-label { font-size: 14px; color: white; font-family: Mulish, sans-serif; } .hotkey-container label { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; text-decoration: dotted; } .hotkey-container label:hover { cursor: url("data:image/png;base64,AAACAAEAICAAAAAAAACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2Nj/2NjY/9jY2P/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNjY/9jY2P/Y2Nj/2NjY/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABjY2P/Y2Nj/2NjY/9jY2P/Y2Nj/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2Nj/2NjY/9jY2P/Y2Nj/2NjY/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNjY/9jY2P/Y2Nj/2NjY/9jY2P/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ6env+enp7/np6e/56env9jY2P/Y2Nj/2NjY/9jY2P/Y2Nj/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMz//////////////////////56env9jY2P/Y2Nj/2NjY/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzM/7W1tf////8+////Pv///z7///8+/////56env9jY2P/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////+enp7/////Pv///z7///8+////Pv///z7///8+/////56env8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////56env////8+////Pv///z7///8+////Pv///z7/////np6e/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////np6e/////z7///8+////Pv///z7///8+////Pv////+enp7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////+enp7/////Pv///z7///8+////Pv///z7///8+/////56env8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////+enp7/////Pv///z7///8+////PrW1tf/MzMz/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////+enp7/np6e/56env+enp7/zMzM/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////8f///+H////B////g////wf//+AP///AH///nj///z8///8/P///Pz///z8///+ef///wP///+H///8=") 0 0, auto; text-decoration: underline; } .extra-container label:hover { text-decoration: underline; cursor: pointer; } .hotkey-label { background: #333333; border: 1px solid #444444; border-radius: 4px; color: white; display: inline-block; font-family: Mulish, sans-serif; font-size: 11px; line-height: 1.5; margin: 0 .1em .1em .1em; padding: .1em .7em; text-indent: 0; cursor: default; text-transform: uppercase; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; box-shadow: inset -2px -2px 1px 0px rgba(0, 0, 0, 0.15); } .menu-container { display: flex; flex-direction: column; } .notyf__toast { transform-origin: center center; will-change: transform, opacity; position: relative; overflow: hidden; transform: none !important; cursor: pointer; max-width: none !important } .notyf__message { animation: none !important; transition: none !important; opacity: 1 !important; transform: none !important; } .notyf__toast--warning { background: orange !important; } .notyf.disabled { display: none !important; } .canvas-blending-switcher { color: white; font-family: Mulish, sans-serif; } .pt-settings-ui select option { background-color: #222222; font-family: Mulish, sans-serif; } `); let img = new Image(); let imgDataURL = null; let posX = 0, posY = 0, scale = 1, opacity = 0.5; let rotation = 0; const overlayElem = document.createElement("canvas"); overlayElem.className = "pt-overlay" const settingsBG = document.createElement("div"); settingsBG.className = "pt-settings-bg" const fileReader = $('<input type="file" accept="image/*" class="file-reader">')[0] const materialIcons = document.createElement('link'); materialIcons.href = 'https://fonts.googleapis.com/icon?family=Material+Icons'; materialIcons.rel = 'stylesheet'; const jQCss = document.createElement('link'); jQCss.rel = 'stylesheet'; jQCss.href = 'https://code.jquery.com/ui/1.14.1/themes/base/jquery-ui.css'; document.head.append(materialIcons, jQCss); const notyf = new Notyf({ ripple: false, duration: 2000, maxVisibleNotifications: 1, position: { x: 'center', y: 'top' } }); GM_addStyle(GM_getResourceText("notyfCSS")); let menuButtons = $('#menu-buttons') const openPTButton = $('<a href="#" title="Open Pixeltracer" id="open-pixeltracer-button" class="grey margin-top-button"><img src="https://i.imgur.com/tLWE5SR.png" alt="icon"></a>') const pixeltracerUI = document.createElement('div'); pixeltracerUI.className = "pt-ui"; pixeltracerUI.innerHTML = ` <div class="header-container"> <div class="header-content-container"> <label class="pt-label">Pixeltracer</label> </div> <div class="ui-button-container"> <button class="settings-btn" title="Minimize..." material-icons>settings</button> <button class="minimize-ui-btn" title="Minimize..." material-icons>minimize</button> <button class="close-ui-btn" title="Close..." material-icons>close</button> </div> </div> <div class="components-container"> <div class="hz-composite-divider"></div> <div class="action-buttons"> <button class="image-upload-btn" title="Open an image..." material-icons>upload</button> <button disabled class="remove-image-btn" title="Remove overlay..." material-icons>delete</button> <button disabled class="hide-overlay-btn" title="Hide overlay..." material-icons>visibility</button> <button disabled class="reset-overlay-btn" title="Reset overlay..." material-icons>cleaning_services</button> <button disabled class="flip-overlay-btn" title="Flip overlay..." material-icons>flip</button> <button disabled class="rotate-overlay-btn" title="Rotate overlay..." material-icons>rotate_90_degrees_cw</button> </div> <div class="tools-container"> <div class="x-container"> <label class="x-header">X: </label> <input class="x-coordinate" type="number" step="1" value="${posX}"></input> </div> <div class="y-container"> <label class="y-header">Y: </label> <input class="y-coordinate" type="number" step="1" value="${posY}"></input> </div> <div class="scale-container"> <label class="scale-header">Scale: </label> <input class="scale-input" type="number" step="0.01" value="${scale}"></input> </div> <div class="opacity-container"> <label class="opacity-header">Opacity: </label> <input class="opacity-input" type="range" min="0" max="1" step="0.01" value="${opacity}"></input> </div> </div> </div> ` const pixeltracerSettingsUI = document.createElement('div'); pixeltracerSettingsUI.className = "pt-settings-ui"; pixeltracerSettingsUI.innerHTML = ` <div class="header-container"> <div class="header-content-container"> <label class="pt-label">Settings</label> </div> </div> <div class="hz-composite-divider"></div> </div> <div class="menu-container"> <div class="settings-container"> <label><input type="checkbox" class="notification-toggle" checked> Show notifications</label> <select class="canvas-blending-switcher"> <option value="" disabled selected>Select blending mode...</option> <option value="normal">Normal</option> <option value="multiply">Multiply</option> <option value="screen">Screen</option> <option value="overlay">Overlay</option> <option value="darken">Darken</option> <option value="lighten">Lighten</option> <option value="color-dodge">Color Dodge</option> <option value="color-burn">Color Burn</option> <option value="hard-light">Hard Light</option> <option value="soft-light">Soft Light</option> <option value="difference">Difference</option> <option value="exclusion">Exclusion</option> <option value="hue">Hue</option> <option value="saturation">Saturation</option> <option value="color">Color</option> <option value="luminosity">Luminosity</option> </select> </div> <div class="header-container"> <div class="header-content-container"> <label class="pt-label">Hotkeys</label> </div> </div> <div class="hz-composite-divider"></div> </div> <div class="hotkey-container"> <span class="hotkey-header-label">Hover over a hotkey to view the info.</span><br> <div class="hotkey-label">SHIFT</div><label title="SHIFT + Right click to jump the overlay to your mouse position.">+ Right Click</label><br> <div class="hotkey-label">SHIFT</div><label title="Hold SHIFT + W + Left click to drag the overlay around.">+ W + Left Click</label><br> <div class="hotkey-label">SHIFT</div><label title="SHIFT + Q to enable auto color picking.">+ Q</label><br> <div class="hotkey-label">CTRL</div><label title="SHIFT + Middle click to select the pixel color at your current mouse position.">+ Middle Click</label> </div> <div class="extra-container"> <div class="hz-composite-divider"></div> <label class="pt-view-1">View script info</label><br> <label class="pt-view-2">PXP Hub Website</label><br> <label class="pt-view-3">PXP Hub Discord</label> </div> </div> </div> </div>` menuButtons.append(openPTButton) document.body.append(pixeltracerUI, settingsBG, pixeltracerSettingsUI, fileReader, pixeltracerUI); /* start of ui interface dragging */ let uiOpened = false; const ptUI = $(".pt-ui")[0]; const ptSettingsUI = $(".pt-settings-ui")[0]; const newOpenPTButton = $("#open-pixeltracer-button")[0]; $(".pt-ui, .pt-settings-ui").draggable({ containment: "window", snap: ".pt-ui,.pt-settings-ui", snapMode: "outer", stack: ".pt-ui,.pt-settings-ui", snapTolerance: 5 }); /* end of ui interface dragging */ newOpenPTButton.addEventListener('click', (e) => { uiOpened = !uiOpened; ptUI.style.display = uiOpened ? 'block' : 'none'; }); /* start of overlay placement handling */ const xValue = ptUI.querySelector('.x-coordinate'); const yValue = ptUI.querySelector('.y-coordinate'); const scaleValue = ptUI.querySelector('.scale-input'); const opacityValue = ptUI.querySelector('.opacity-input'); xValue.addEventListener("input", () => { posX = parseFloat(xValue.value) || 0; drawOverlay(); }); yValue.addEventListener("input", () => { posY = parseFloat(yValue.value) || 0; drawOverlay(); }); scaleValue.addEventListener("input", () => { const value = parseFloat(scaleValue.value); scale = value <= 0 ? 1 : value; drawOverlay(); }); opacityValue.addEventListener("input", () => { overlayElem.style.opacity = parseFloat(opacityValue.value); drawOverlay(); }); /* end of overlay placement handling */ function antiNotificationStack() { document.querySelectorAll('.notyf__toast').forEach(el => el.remove()); } function convertHexToRGB(hex) { const regex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return regex ? [parseInt(regex[1], 16), parseInt(regex[2], 16), parseInt(regex[3], 16)] : null; } function getConvertedRGBs() { const pixelplacePaletteColors = []; document.querySelectorAll("#palette-buttons a").forEach(a => { const hexFinder = a.getAttribute("title"); const startRGBConversion = convertHexToRGB(hexFinder); if (startRGBConversion) pixelplacePaletteColors.push(startRGBConversion); }); return pixelplacePaletteColors; } function scaleToCanvas() { const originalCanvas = document.getElementById("canvas"); if (!originalCanvas) return; overlayElem.width = originalCanvas.width; overlayElem.height = originalCanvas.height; overlayElem.style.width = `${originalCanvas.width}px`; overlayElem.style.height = `${originalCanvas.height}px`; overlayElem.style.imageRendering = "pixelated"; const container = document.getElementById("painting-move"); if (container && !container.contains(overlayElem)) { container.appendChild(overlayElem); } } function getNearestPaletteColor(r, g, b, palette) { let minDist = Infinity, best = [r, g, b]; for (const [pr, pg, pb] of palette) { const dist = (r - pr) ** 2 + (g - pg) ** 2 + (b - pb) ** 2; if (dist < minDist) { minDist = dist; best = [pr, pg, pb]; } } return best; } let flipped = false; let rotated = false; function drawOverlay() { if (!imgDataURL || !img.complete || img.naturalWidth === 0) return; const ctx = overlayElem.getContext("2d"); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.imageSmoothingEnabled = false; ctx.clearRect(0, 0, overlayElem.width, overlayElem.height); const off = document.createElement("canvas"); off.width = img.naturalWidth; off.height = img.naturalHeight; const offCtx = off.getContext("2d"); offCtx.drawImage(img, 0, 0); let imageData = offCtx.getImageData(0, 0, off.width, off.height); const palette = getConvertedRGBs(); const drawX = Math.round(posX); const drawY = Math.round(posY); const drawW = Math.round(off.width * scale); const drawH = Math.round(off.height * scale); ctx.save(); ctx.translate(drawX + drawW / 2, drawY + drawH / 2); ctx.rotate(rotation * Math.PI / 180); if (flipped) { ctx.scale(-1, 1); } ctx.drawImage( off, 0, 0, off.width, off.height, -drawW / 2, -drawH / 2, drawW, drawH ); ctx.restore(); }; img.onload = () => { scaleToCanvas() drawOverlay() } const closeUIButton = ptUI.querySelector(".close-ui-btn"); closeUIButton.onclick = () => { pixeltracerUI.style.display = "none"; uiOpened = false; } let minimized = false; let hidden = false; let opened = false; let notyfChecked = false; let notificationsEnabled = true; const components = ptUI.querySelector(".components-container"); const minimizeUIButton = ptUI.querySelector(".minimize-ui-btn"); minimizeUIButton.onclick = () => { minimized = !minimized; components.style.display = minimized ? 'none' : 'block'; } const openImageButton = ptUI.querySelector(".image-upload-btn"); openImageButton.onclick = () => fileReader.click(); fileReader.onchange = e => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = evt => { removeImageButton.disabled = false; hideOverlayButton.disabled = false; resetOverlayButton.disabled = false; flipOverlayButton.disabled = false; rotateOverlayButton.disabled = false; imgDataURL = evt.target.result; img.src = imgDataURL; overlayElem.style.opacity = parseFloat(opacityValue.value); }; reader.readAsDataURL(file); }; const removeImageButton = ptUI.querySelector(".remove-image-btn"); removeImageButton.onclick = () => { imgDataURL = ""; img.src = ""; removeImageButton.disabled = true; hideOverlayButton.disabled = true; resetOverlayButton.disabled = true; flipOverlayButton.disabled = true; rotateOverlayButton.disabled = true; overlayElem.getContext("2d").clearRect(0, 0, overlayElem.width, overlayElem.height); fileReader.value = ""; antiNotificationStack(); notyf.success(`Overlay removed successfully`); }; const rotateOverlayButton = ptUI.querySelector(".rotate-overlay-btn"); rotateOverlayButton.onclick = () => { rotation = (rotation + 90) % 360; drawOverlay(); } const hideOverlayButton = ptUI.querySelector(".hide-overlay-btn"); hideOverlayButton.onclick = () => { opened = !opened; hideOverlayButton.textContent = opened ? 'visibility_off' : 'visibility'; overlayElem.style.display = opened ? 'none' : 'block'; if (opened) { antiNotificationStack(); notyf.open({ type: 'warning', className: 'notyf__toast--warning', message: `Overlay hidden at coordinate: ${posX} & ${posY}`, icon: { className: 'material-icons', tagName: 'i', text: 'warning' }}); } else { antiNotificationStack(); notyf.success(`Overlay shown at coordinate: ${posX} & ${posY}`); } }; const settingsButton = ptUI.querySelector(".settings-btn"); settingsButton.onclick = () => { hidden = !hidden; pixeltracerSettingsUI.style.display = hidden ? 'flex' : 'none'; }; const resetOverlayButton = ptUI.querySelector(".reset-overlay-btn"); resetOverlayButton.onclick = () => { posX = 0; ptUI.querySelector('.x-coordinate').value = `${posX}`; posY = 0; ptUI.querySelector('.y-coordinate').value = `${posY}`; scale = 1; ptUI.querySelector('.scale-input').value = `${scale}`; overlayElem.style.opacity = "0.5"; ptUI.querySelector('.opacity-input').value = `${opacity}`; rotation = 0; flipped = false; drawOverlay() antiNotificationStack(); notyf.success(`Overlay reset successfully`); }; const notificationsCheckbox = ptSettingsUI.querySelector(".notification-toggle"); notificationsCheckbox.onclick = () => { notificationsEnabled = !notificationsEnabled; if (!notificationsEnabled) { document.querySelectorAll('.notyf__toast').forEach(el => el.remove()); notyf.dismissAll(); } }; const flipOverlayButton = ptUI.querySelector(".flip-overlay-btn"); flipOverlayButton.onclick = () => { flipped = !flipped; drawOverlay(); } const bindNotyf = notyf.open.bind(notyf); notyf.open = function (options) { if (!notificationsEnabled) return null; return bindNotyf(options); }; document.addEventListener('click', (e) => { if (!notificationsEnabled) return; const toast = e.target.closest('.notyf__toast'); if (toast) { toast.remove(); } }); const viewScriptInfoLabel = ptSettingsUI.querySelector(".pt-view-1"); const viewScriptWebsite = ptSettingsUI.querySelector(".pt-view-2"); const viewScriptDiscord = ptSettingsUI.querySelector(".pt-view-3"); viewScriptInfoLabel.onclick = () => { alert(`You are currently running version 0.20.0 of Pixeltracer`); }; viewScriptWebsite.onclick = () => { if (!confirm("Visit the PXP Hub Website? This will redirect you off this page.")) return; window.open("https://pxphub.neocities.org/pixeltracer", "_blank"); }; viewScriptDiscord.onclick = () => { if (!confirm("Join the PXP Hub Community Discord? This will redirect you off this page.")) return; window.open("https://discord.gg/KgYavKWXSN", "_blank"); }; const overlayBlendingSelect = document.querySelector(".canvas-blending-switcher"); overlayBlendingSelect.addEventListener("change", e => { overlayElem.style.mixBlendMode = e.target.value; }); let colorPick = null; let isDragging = false; let dragOffsetX = 0; let dragOffsetY = 0; let wPressed = false; let autoColorPick = false; // e button 0 is left click, e button 1 is middle click, e button 2 is right click // turned the color picking block into a global function so I don't have to copy and paste the block everytime function colorPickFunc(clickX, clickY) { let relX = Math.floor((clickX - posX) / scale); let relY = Math.floor((clickY - posY) / scale); const w = img.naturalWidth || img.width; const h = img.naturalHeight || img.height; const cx = w / 2; const cy = h / 2; if (rotation) { const rad = -rotation * Math.PI / 180; let dx = relX - cx; let dy = relY - cy; const cos = Math.cos(rad); const sin = Math.sin(rad); relX = Math.floor(dx * cos - dy * sin + cx); relY = Math.floor(dx * sin + dy * cos + cy); } if (flipped) { relX = w - 1 - relX; } const srcW = w; const srcH = h; if (relX < 0 || relY < 0 || relX >= srcW || relY >= srcH) return; const tempCanvas = document.createElement("canvas"); tempCanvas.width = srcW; tempCanvas.height = srcH; const tempCtx = tempCanvas.getContext("2d"); tempCtx.drawImage(img, 0, 0); const data = tempCtx.getImageData(relX, relY, 1, 1).data; const [r, g, b, a] = data; if (a < 10) return; colorPick = { r, g, b }; } function selectFoundColor() { if (!colorPick) return; const { r, g, b } = colorPick; const palette = getConvertedRGBs(); const [nr, ng, nb] = getNearestPaletteColor(r, g, b, palette); document.querySelectorAll("#palette-buttons a").forEach(a => { const hex = a.getAttribute("title"); const rgb = convertHexToRGB(hex); if (!rgb) return; const [pr, pg, pb] = rgb; if (pr === nr && pg === ng && pb === nb) { a.click(); } }); } document.addEventListener("keydown", e => { if (e.key?.toLowerCase() === "w") { wPressed = true }; if (e.key?.toLowerCase() === "q" && e.shiftKey && !e.repeat) { autoColorPick = !autoColorPick; if (autoColorPick) { antiNotificationStack(); notyf.success('Auto click picking enabled');} else if (!autoColorPick) { antiNotificationStack(); notyf.open({ type: 'warning', className: 'notyf__toast--warning', message: `Auto click picking disabled`, icon: { className: 'material-icons', tagName: 'i', text: 'warning' } });} }; }); document.addEventListener("keyup", e => { if (e.key?.toLowerCase() === "w") { wPressed = false }; }); let holdNotificationActive = false; document.addEventListener("keydown", e => { if (e.shiftKey && wPressed && overlayElem?.isConnected) { if (!holdNotificationActive) { antiNotificationStack(); notyf.open({ type: 'warning', className: 'notyf__toast--warning', message: `Hold to drag mode enabled`, icon: { className: 'material-icons', tagName: 'i', text: 'warning' } }); holdNotificationActive = true; } const painting = document.querySelector("#painting"); if (painting) painting.style.pointerEvents = "none"; } }); document.addEventListener("keyup", e => { if (holdNotificationActive) { const painting = document.querySelector("#painting"); if (painting) painting.style.pointerEvents = ""; antiNotificationStack(); notyf.success('Hold to drag mode disabled'); holdNotificationActive = false; } }); document.addEventListener("mousedown", e => { if (!imgDataURL) return; const canvas = document.getElementsByClassName("pt-overlay")[0]; if (!canvas) return; const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; const clickX = (e.clientX - rect.left) * scaleX; const clickY = (e.clientY - rect.top) * scaleY; if (e.shiftKey && e.button === 2 && e.button !== 1) { posX = Math.round(clickX); posY = Math.round(clickY); drawOverlay(); xValue.value = posX; yValue.value = posY; } else if (e.button === 1 && e.ctrlKey) { e.preventDefault(); colorPickFunc(clickX, clickY); } else if (e.shiftKey && wPressed && e.button === 0) { isDragging = true; e.preventDefault(); dragOffsetX = clickX - posX; dragOffsetY = clickY - posY; } }); document.addEventListener("mousemove", e => { if (!isDragging) return; const canvas = document.getElementsByClassName("pt-overlay")[0]; if (!canvas) return; const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; const moveX = (e.clientX - rect.left) * scaleX; const moveY = (e.clientY - rect.top) * scaleY; if (isDragging) { posX = Math.round(moveX - dragOffsetX); posY = Math.round(moveY - dragOffsetY); drawOverlay(); xValue.value = posX; yValue.value = posY; } }); let lastPickedColor = null; document.addEventListener("mousemove", e => { const canvas = document.getElementsByClassName("pt-overlay")[0]; if (!canvas) return; const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; const moveX = (e.clientX - rect.left) * scaleX; const moveY = (e.clientY - rect.top) * scaleY; if (autoColorPick) { colorPickFunc(moveX, moveY); } if (colorPick) { const { r, g, b } = colorPick; if (!lastPickedColor || r !== lastPickedColor.r || g !== lastPickedColor.g || b !== lastPickedColor.b) { lastPickedColor = { r, g, b }; selectFoundColor(); } } }); document.addEventListener("mouseup", () => { if (isDragging) { isDragging = false; } if (colorPick) { selectFoundColor() colorPick = null; } }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址