|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> |
|
<title>蒙版王</title> |
|
<style> |
|
|
|
@media (max-width: 767px) { |
|
body { |
|
--bg-color: var(--tg-theme-bg-color); |
|
font: 12px/18px "Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, Verdana, sans-serif; |
|
background-color: var(--bg-color); |
|
color: var(--tg-theme-text-color); |
|
|
|
width: 350; |
|
margin: 0 auto; |
|
} |
|
#canvas { |
|
border: 1px solid #000; |
|
width: 100%; |
|
height: auto; |
|
} |
|
html, body { |
|
overflow-x: hidden; |
|
} |
|
#uploadButton{ |
|
width: 80%; |
|
} |
|
#save{ |
|
width: 20%; |
|
} |
|
.myDiv { |
|
width: 98%; |
|
display: flex; |
|
flex-direction: row; |
|
justify-content: flex-start; |
|
margin: 0px 1%; |
|
flex-wrap: wrap; |
|
} |
|
.myDiv1 { |
|
width: 98%; |
|
display: flex; |
|
flex-direction: row; |
|
justify-content: flex-start; |
|
margin: 0px 1%; |
|
} |
|
.overlay { |
|
left: 20px; |
|
} |
|
} |
|
|
|
@media (min-width: 768px) { |
|
#canvas { |
|
border: 1px solid #000; |
|
width: auto; |
|
height: auto; |
|
} |
|
.myDiv { |
|
display: flex; |
|
flex-direction: row; |
|
justify-content: center; |
|
margin: 0 100px; |
|
width: 512px; |
|
flex-wrap: wrap; |
|
} |
|
.myDiv1 { |
|
display: flex; |
|
flex-direction: row; |
|
justify-content: center; |
|
margin: 0 100px; |
|
width: 512px; |
|
} |
|
.overlay { |
|
left: 100px; |
|
} |
|
} |
|
#circle { |
|
position: absolute; |
|
width: 10px; |
|
height: 10px; |
|
border-radius: 50%; |
|
border: 2px solid red; |
|
background-color: transparent; |
|
transition: opacity 1s; |
|
pointer-events: none; |
|
} |
|
.show { |
|
opacity: 1; |
|
} |
|
.hide { |
|
opacity: 0; |
|
} |
|
.overlay { |
|
position: absolute; |
|
top: 82px; |
|
z-index: 10; |
|
|
|
|
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="myDiv1"> |
|
<button id="uploadButton" style="display: None;"> |
|
选择图片 |
|
<input type="file" id="upload" accept="image/*" style="position: absolute; top: 0; left: 0; width: 98%; height: 100%; opacity: 0; cursor: pointer;"> |
|
</button> |
|
<button id="save" style="height: 50px;display: None">保存蒙版</button> |
|
<button id="saveToClipboard" style="height: 50px;box-sizing: border-box; padding: 10px; width: 98%; font-size: 16px; height: 50px; |
|
line-height: 30px; overflow: hidden; position: relative;">上传蒙版 Upload mask</button> |
|
</div> |
|
<div class="myDiv" id="tools"> |
|
<input type="range" id="brushSizeSlider" style="width: 100%" value="40" min="1" max="150" step="1" > |
|
</div> |
|
<br> |
|
<br> |
|
<div class="myDiv" id="myImg1"> |
|
<canvas id="canvas"></canvas> |
|
<br> |
|
<img id="outputImg" ></img> |
|
</div> |
|
<div class="overlay" style="display: flex; flex-direction: row; align-items: flex-start;"> |
|
<label style="margin-bottom: 10px;"> |
|
<input type="radio" name="editMode" value="draw" checked> 画笔模式 |
|
</label> |
|
<label style="margin-bottom: 10px;"> |
|
<input type="radio" name="editMode" value="erase"> 擦除模式 |
|
</label> |
|
<label> |
|
<input type="radio" name="editMode" value="select"> 不编辑 |
|
</label> |
|
</div> |
|
<div id="circle" style="width: 20px; height: 20px; border-radius: 50%; border: 2px solid red;"></div> |
|
<script src="https://telegram.org/js/telegram-web-app.js"></script> |
|
<script> |
|
|
|
Telegram.WebApp.ready(); |
|
Telegram.WebApp.expand(); |
|
Telegram.WebApp.enableClosingConfirmation() |
|
Telegram.WebApp.HapticFeedback.impactOccurred("medium"); |
|
|
|
Telegram.WebApp.onEvent('themeChanged', function() { |
|
document.documentElement.className = Telegram.WebApp.colorScheme; |
|
}); |
|
function getQueryParameter(name) { |
|
const urlParams = new URLSearchParams(window.location.search); |
|
return urlParams.get(name); |
|
} |
|
async function uploadMask(base64String) { |
|
const response = await fetch('https://api.imgur.com/3/image', { |
|
method: 'POST', |
|
headers: { |
|
'Authorization': 'Client-ID 955c061744537ff', |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ image: base64String }), |
|
}); |
|
const r = await response.json() |
|
return r.data.link; |
|
} |
|
window.onerror = function (message, source, lineno, colno, error) { |
|
const errorData = { |
|
message: message, |
|
source: source, |
|
lineno: lineno, |
|
colno: colno, |
|
stack: error ? error.stack : null |
|
}; |
|
console.error("JavaScript Error:", errorData); |
|
|
|
fetch('/log-error', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json' |
|
}, |
|
body: JSON.stringify(errorData) |
|
}); |
|
}; |
|
window.onload = function() { |
|
var canvas = document.getElementById('canvas'); |
|
var context = canvas.getContext('2d'); |
|
var image = document.getElementById('outputImg'); |
|
var imageMask = new Image(); |
|
var maskData = null; |
|
var isDrawing = false; |
|
var brushSize = 40; |
|
var intervalHandel = null; |
|
var brushSizeSlider = document.getElementById('brushSizeSlider'); |
|
var editModeRadios = document.getElementsByName('editMode'); |
|
var selectedMode = "draw"; |
|
var isRotate = false; |
|
function isMobile() { |
|
return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)); |
|
} |
|
for (var i = 0; i < editModeRadios.length; i++) { |
|
editModeRadios[i].addEventListener('change', function() { |
|
|
|
selectedMode = this.value; |
|
drawImagesInterval(100); |
|
}); |
|
} |
|
brushSizeSlider.addEventListener('input', function() { |
|
brushSize = parseInt(this.value); |
|
setCircleSize(brushSize); |
|
if (isMobile()){ |
|
var canvasRect = canvas.getBoundingClientRect(); |
|
var scaleX = canvas.width / canvasRect.width; |
|
showCircle(175 + window.scrollX, 200 + window.scrollY, scaleX); |
|
} |
|
}); |
|
function resizeImage(img,s,resizedImage) { |
|
var maxWidth = s; |
|
var maxHeight = s; |
|
var width = img.width; |
|
var height = img.height; |
|
if (width/height < 4/3 && width <= maxWidth && height <= maxHeight) { |
|
return false; |
|
} |
|
if (!isMobile() && width <= maxWidth && height <= maxHeight) { |
|
return false; |
|
} |
|
if (width > maxWidth || height > maxHeight || width/height >= 4/3) { |
|
var ratio = Math.max(maxWidth / width, maxHeight / height); |
|
if (width <= maxWidth && height <= maxHeight){ |
|
ratio = 1; |
|
} |
|
width = Math.floor(width * ratio); |
|
height = Math.floor(height * ratio); |
|
var tempCanvas = document.createElement('canvas'); |
|
tempCanvas.width = width; |
|
tempCanvas.height = height; |
|
var ctx = tempCanvas.getContext('2d'); |
|
isRotate = false; |
|
if (width/height >= 4/3 && isMobile()){ |
|
tempCanvas.height = width; |
|
tempCanvas.width = height; |
|
ctx.clearRect(0, 0, tempCanvas.width, tempCanvas.height); |
|
ctx.save(); |
|
ctx.translate(tempCanvas.width / 2, tempCanvas.height / 2); |
|
ctx.rotate(Math.PI / 2); |
|
ctx.drawImage(img, -img.width / 2, -img.height / 2); |
|
ctx.restore(); |
|
isRotate = true; |
|
} |
|
else { |
|
ctx.drawImage(img, 0, 0, width, height); |
|
} |
|
resizedImage.src = tempCanvas.toDataURL(); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
function drawImages() { |
|
context.clearRect(0, 0, canvas.width, canvas.height); |
|
context.putImageData(maskData,0,0); |
|
if (selectedMode == "select") { |
|
return; |
|
} |
|
context.globalAlpha = 0.75; |
|
context.drawImage(image, 0, 0); |
|
|
|
context.globalAlpha = 1; |
|
} |
|
function drawImagesInterval(t) { |
|
if (intervalHandel != null){ |
|
drawImagesTimeOut(100); |
|
window.clearInterval(intervalHandel); |
|
intervalHandel = null; |
|
} |
|
intervalHandel = window.setInterval(function() {drawImages();}, t); |
|
} |
|
window.addEventListener('scroll', function(event) { |
|
var scrollTop = window.pageYOffset || document.documentElement.scrollTop; |
|
var overlay = document.querySelector(".overlay"); |
|
overlay.style.top = (scrollTop + 82) + "px"; |
|
}); |
|
function stopDrawImagesInterval() { |
|
if (intervalHandel != null){ |
|
drawImagesTimeOut(100); |
|
window.clearInterval(intervalHandel); |
|
intervalHandel = null; |
|
} |
|
} |
|
function drawImagesTimeOut(t) { |
|
window.setTimeout(function() {drawImages();}, t); |
|
} |
|
function getImageDataB(img) { |
|
context.clearRect(0, 0, canvas.width, canvas.height); |
|
var width = image.width; |
|
var height = image.height; |
|
canvas.width = width; |
|
canvas.height = height; |
|
context.drawImage(img, 0, 0); |
|
var imageData = context.getImageData(0, 0, width, height); |
|
return imageData; |
|
} |
|
function getImageData(img) { |
|
var tempCanvas = document.createElement('canvas'); |
|
tempCanvas.width = canvas.width; |
|
tempCanvas.height = canvas.height; |
|
var tempContext = tempCanvas.getContext('2d'); |
|
var width = image.width; |
|
var height = image.height; |
|
tempCanvas.width = width; |
|
tempCanvas.height = height; |
|
tempContext.drawImage(img, 0, 0); |
|
var imageData = tempContext.getImageData(0, 0, width, height); |
|
return imageData; |
|
} |
|
function createMaskImageData(width, height) { |
|
|
|
var imageData = new ImageData(width, height); |
|
|
|
var data = imageData.data; |
|
|
|
for (var i = 0; i < data.length; i += 4) { |
|
data[i] = 0; |
|
data[i + 1] = 0; |
|
data[i + 2] = 0; |
|
data[i + 3] = 255; |
|
} |
|
return imageData; |
|
} |
|
function getImageDataUrl(imageData) { |
|
var tempCanvas = document.createElement('canvas'); |
|
tempCanvas.width = canvas.width; |
|
tempCanvas.height = canvas.height; |
|
var tempContext = tempCanvas.getContext('2d'); |
|
tempContext.putImageData(imageData, 0, 0); |
|
return tempCanvas.toDataURL(); |
|
} |
|
async function loadImageFromUrl0(imageUrl) { |
|
const dataUrl = await convertImageUrlToDataUrl(imageUrl); |
|
await loadImageFromUrl(dataUrl); |
|
} |
|
async function loadImageFromUrl(dataUrl) { |
|
image.crossOrigin='anonymous'; |
|
image.onload = function() { |
|
var resizedImage = new Image(); |
|
resizedImage.onload = _ok; |
|
function _ok(event){ |
|
image = resizedImage; |
|
canvas.width = resizedImage.width; |
|
canvas.height = resizedImage.height; |
|
maskData = createMaskImageData(image.width,image.height); |
|
imageMask.src = getImageDataUrl(maskData); |
|
drawImagesTimeOut(100); |
|
} |
|
if (!resizeImage(image,512,resizedImage)) { |
|
canvas.width = image.width; |
|
canvas.height = image.height; |
|
maskData = createMaskImageData(image.width,image.height); |
|
imageMask.src = getImageDataUrl(maskData); |
|
drawImagesTimeOut(100); |
|
} |
|
} |
|
image.src = dataUrl; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function handleFile(file) { |
|
var reader = new FileReader(); |
|
reader.onload = function(event) { |
|
image.onload = function() { |
|
var resizedImage = new Image(); |
|
resizedImage.onload = _ok; |
|
function _ok(event){ |
|
image = resizedImage; |
|
canvas.width = resizedImage.width; |
|
canvas.height = resizedImage.height; |
|
maskData = createMaskImageData(image.width,image.height); |
|
imageMask.src = getImageDataUrl(maskData); |
|
drawImagesTimeOut(100); |
|
} |
|
if (!resizeImage(image,512,resizedImage)) { |
|
canvas.width = image.width; |
|
canvas.height = image.height; |
|
maskData = createMaskImageData(image.width,image.height); |
|
imageMask.src = getImageDataUrl(maskData); |
|
drawImagesTimeOut(100); |
|
} |
|
} |
|
image.src = event.target.result; |
|
console.log(image.src); |
|
} |
|
reader.readAsDataURL(file); |
|
} |
|
|
|
|
|
document.getElementById("upload").addEventListener('change', function(e) { |
|
var file = e.target.files[0]; |
|
handleFile(file); |
|
}); |
|
|
|
|
|
document.addEventListener('dragover', function(e) { |
|
e.preventDefault(); |
|
}); |
|
document.addEventListener('drop', function(e) { |
|
e.preventDefault(); |
|
|
|
var file = e.dataTransfer.files[0]; |
|
|
|
handleFile(file); |
|
}); |
|
|
|
function inRect(x,y,rect){ |
|
return ( |
|
x >= rect.left && |
|
x <= rect.right && |
|
y >= rect.top && |
|
y <= rect.bottom |
|
); |
|
} |
|
function calcCanvasOffset(e,canvas) { |
|
var canvasRect = canvas.getBoundingClientRect(); |
|
var scaleX = canvas.width / canvasRect.width; |
|
var scaleY = canvas.height / canvasRect.height; |
|
var offsetX = (e.clientX - canvasRect.left) * scaleX; |
|
var offsetY = (e.clientY - canvasRect.top) * scaleY; |
|
return [offsetX,offsetY,scaleX,scaleY]; |
|
} |
|
|
|
function onTouchDown(e) { |
|
if (selectedMode == "select") { |
|
return; |
|
} |
|
var canvasRect = canvas.getBoundingClientRect(); |
|
var [offsetX,offsetY,scaleX,scaleY] = calcCanvasOffset(e,canvas); |
|
|
|
if (!inRect(e.clientX,e.clientY,canvasRect)){ |
|
return; |
|
} |
|
if (draw(offsetX, offsetY)) { |
|
isDrawing = true; |
|
drawImagesInterval(100); |
|
} |
|
} |
|
document.addEventListener('touchstart', function(event) { |
|
var touch = event.touches[0]; |
|
onTouchDown(touch); |
|
}); |
|
document.addEventListener('mousedown',function(event){ |
|
onTouchDown(event); |
|
} ); |
|
|
|
document.addEventListener('mouseup', function() { |
|
isDrawing = false; |
|
stopDrawImagesInterval(); |
|
}); |
|
|
|
function onMove(e) { |
|
var [offsetX,offsetY,scaleX,scaleY] = calcCanvasOffset(e,canvas); |
|
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { |
|
showCircle(e.clientX + window.scrollX, e.clientY + window.scrollY, scaleX); |
|
} else { |
|
showCircle(e.clientX + window.scrollX, e.clientY + window.scrollY, scaleX); |
|
} |
|
var canvasRect = canvas.getBoundingClientRect(); |
|
if (isDrawing) { |
|
|
|
if (!inRect(e.clientX,e.clientY,canvasRect)){ |
|
return; |
|
} |
|
draw(offsetX, offsetY); |
|
} |
|
} |
|
canvas.addEventListener('touchmove', function(event) { |
|
if (selectedMode == "select") { |
|
return; |
|
} |
|
event.preventDefault(); |
|
var touch = event.touches[0]; |
|
onMove(touch); |
|
}, { passive: false }); |
|
if (!isMobile()) { |
|
document.addEventListener('mousemove', function(event){ |
|
onMove(event); |
|
}); |
|
} |
|
|
|
|
|
|
|
function draw(x, y) { |
|
if (selectedMode == "selected") { |
|
return false; |
|
} |
|
if (maskData == null) { |
|
return false; |
|
} |
|
var data = maskData.data; |
|
var brushRadius = brushSize / 2; |
|
|
|
for (var i = -brushRadius; i <= brushRadius; i++) { |
|
for (var j = -brushRadius; j <= brushRadius; j++) { |
|
var pixelX = Math.round(x + i); |
|
var pixelY = Math.round(y + j); |
|
|
|
if (pixelX < 0 || pixelX >= canvas.width || pixelY < 0 || pixelY >= canvas.height) { |
|
continue; |
|
} |
|
var distance = Math.sqrt((pixelX - x) * (pixelX - x) + (pixelY - y) * (pixelY - y)); |
|
if (distance > brushRadius) { |
|
continue; |
|
} |
|
var index = (pixelY * canvas.width + pixelX) * 4; |
|
if (selectedMode == "draw"){ |
|
data[index] = 255; |
|
data[index + 1] = 255; |
|
data[index + 2] = 255; |
|
} |
|
else { |
|
data[index] = 0; |
|
data[index + 1] = 0; |
|
data[index + 2] = 0; |
|
} |
|
|
|
|
|
} |
|
} |
|
|
|
return true; |
|
} |
|
function generateRandomFileName() { |
|
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
|
var length = 10; |
|
var fileName = ''; |
|
for (var i = 0; i < length; i++) { |
|
var randomIndex = Math.floor(Math.random() * characters.length); |
|
fileName += characters.charAt(randomIndex); |
|
} |
|
return fileName; |
|
} |
|
function rotateImageData(imageData) { |
|
const { width, height } = imageData; |
|
const rotatedData = new Uint8ClampedArray(width * height * 4); |
|
|
|
for (let y = 0; y < height; y++) { |
|
for (let x = 0; x < width; x++) { |
|
const srcIndex = (x + y * width) * 4; |
|
const destIndex = ((width - x - 1) * height + y) * 4; |
|
|
|
rotatedData[destIndex] = imageData.data[srcIndex]; |
|
rotatedData[destIndex + 1] = imageData.data[srcIndex + 1]; |
|
rotatedData[destIndex + 2] = imageData.data[srcIndex + 2]; |
|
rotatedData[destIndex + 3] = imageData.data[srcIndex + 3]; |
|
} |
|
} |
|
|
|
return new ImageData(rotatedData, height, width); |
|
} |
|
|
|
function openImageInNewTab(dataUrl) { |
|
const newTab = window.open(); |
|
newTab.document.write('<html><body style="margin: 0;"><img src="' + dataUrl + '"></body></html>'); |
|
newTab.document.close(); |
|
} |
|
function saveImageAsFile(dataUrl, filename) { |
|
|
|
const blob = dataURLToBlob(dataUrl); |
|
|
|
|
|
const link = document.createElement('a'); |
|
link.href = URL.createObjectURL(blob); |
|
link.download = filename; |
|
|
|
|
|
link.click(); |
|
|
|
|
|
URL.revokeObjectURL(link.href); |
|
} |
|
|
|
function dataURLToBlob(dataUrl) { |
|
console.log(dataUrl); |
|
const commaIndex = dataUrl.indexOf(','); |
|
const mime = dataUrl.substring(5, commaIndex); |
|
const base64Data = dataUrl.substring(commaIndex + 1); |
|
const byteCharacters = atob(base64Data); |
|
const byteNumbers = new Array(byteCharacters.length); |
|
|
|
for (let i = 0; i < byteCharacters.length; i++) { |
|
byteNumbers[i] = byteCharacters.charCodeAt(i); |
|
} |
|
|
|
const byteArray = new Uint8Array(byteNumbers); |
|
|
|
return new Blob([byteArray], { type: mime }); |
|
} |
|
|
|
document.getElementById('saveToClipboard').addEventListener('click',async function() { |
|
var data = maskData.data; |
|
var tempCanvas = document.createElement('canvas'); |
|
var ctx = tempCanvas.getContext('2d'); |
|
if (isRotate == true){ |
|
var rData = rotateImageData(maskData); |
|
tempCanvas.height = maskData.width; |
|
tempCanvas.width = maskData.height; |
|
ctx.putImageData(rData, 0, 0); |
|
} |
|
else { |
|
tempCanvas.width = canvas.width; |
|
tempCanvas.height = canvas.height; |
|
ctx.putImageData(maskData, 0, 0); |
|
} |
|
|
|
const response = await fetch(tempCanvas.toDataURL("image/png")); |
|
const blob = await response.blob(); |
|
const item = new ClipboardItem({ [blob.type]: blob }); |
|
await navigator.clipboard.write([item]); |
|
|
|
const img = document.getElementById('outputImg'); |
|
img.src = tempCanvas.toDataURL("image/jpeg"); |
|
const base64String = img.src.split(',')[1]; |
|
const response2 = await fetch( |
|
'/uploadVPMask/' + Telegram.WebApp.initDataUnsafe.user.id,{ |
|
|
|
method: 'POST', |
|
headers: { |
|
'Authorization': 'Client-ID 955c061744537ff', |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ image: base64String }), |
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const myImg1 = document.getElementById('myImg1'); |
|
const firstChild = myImg1.firstChild; |
|
let sendback = document.createElement('a'); |
|
sendback.href = 'https://t.me/ainudevideo_bot?start=mask_ok'; |
|
sendback.textContent = 'sendback'; |
|
sendback.click(); |
|
sendback.innerHTML = "如果点击按钮没有反应,请点这个链接 If there is no response when clicking the button, please click on this link"; |
|
myImg1.insertBefore(sendback, firstChild); |
|
|
|
}); |
|
|
|
|
|
document.getElementById('save').addEventListener('click', function() { |
|
var data = maskData.data; |
|
var tempCanvas = document.createElement('canvas'); |
|
var ctx = tempCanvas.getContext('2d'); |
|
if (isRotate == true){ |
|
var rData = rotateImageData(maskData); |
|
tempCanvas.height = maskData.width; |
|
tempCanvas.width = maskData.height; |
|
ctx.putImageData(rData, 0, 0); |
|
} |
|
else { |
|
tempCanvas.width = canvas.width; |
|
tempCanvas.height = canvas.height; |
|
ctx.putImageData(maskData, 0, 0); |
|
} |
|
var link = document.createElement('a'); |
|
link.href = tempCanvas.toDataURL("image/jpeg", 0.9); |
|
link.download = 'mask_' + generateRandomFileName() + '.jpg'; |
|
link.click(); |
|
var oldMode = selectedMode; |
|
selectedMode = "select"; |
|
drawImages(); |
|
selectedMode = oldMode; |
|
|
|
|
|
}); |
|
|
|
var circle = document.getElementById('circle'); |
|
|
|
|
|
function setCircleSize(brushSize) { |
|
} |
|
|
|
|
|
function showCircle(x, y, scale) { |
|
circle.style.left = (x - brushSize/scale/2) + 'px'; |
|
circle.style.top = (y - brushSize/scale/2) + 'px'; |
|
circle.style.width = (brushSize / scale) + 'px'; |
|
circle.style.height = (brushSize / scale) + 'px'; |
|
circle.classList.add('show'); |
|
circle.classList.remove('hide'); |
|
|
|
|
|
setTimeout(hideCircle, 1000); |
|
} |
|
|
|
|
|
function hideCircle() { |
|
circle.classList.remove('show'); |
|
circle.classList.add('hide'); |
|
} |
|
|
|
|
|
function handleTouchStart(e) { |
|
|
|
if (e.touches.length === 1) { |
|
var touch = e.touches[0]; |
|
var x = touch.clientX; |
|
var y = touch.clientY; |
|
var [offsetX,offsetY,scaleX,scaleY] = calcCanvasOffset(touch,canvas); |
|
|
|
showCircle(x + window.scrollX, y + window.scrollY, scaleX); |
|
} |
|
} |
|
async function convertImageUrlToDataUrl(imageUrl) { |
|
const response = await fetch(imageUrl); |
|
const blob = await response.blob(); |
|
return new Promise((resolve, reject) => { |
|
const reader = new FileReader(); |
|
reader.onloadend = () => resolve(reader.result); |
|
reader.onerror = (error) => reject(`Error reading blob: ${error}`); |
|
reader.readAsDataURL(blob); |
|
}); |
|
} |
|
|
|
|
|
setCircleSize(brushSize); |
|
hideCircle(); |
|
|
|
|
|
|
|
|
|
const imageUrl = '/getVPImage/' + Telegram.WebApp.initDataUnsafe.user.id; |
|
if (imageUrl) { |
|
loadImageFromUrl(imageUrl); |
|
} |
|
|
|
|
|
} |
|
</script> |
|
|
|
|
|
|
|
</body> |
|
</html> |