.
раскраска
Сообщений 1 страница 5 из 5
Поделиться22025-10-26 21:51:47
[html]<iframe srcdoc="
<html>
<head>
<meta charset='utf-8'>
<style>
body{margin:0;padding:0;background:#111;color:#eee;font-family:Arial,Helvetica,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh}
#drawApp{background:#222;border-radius:10px;box-shadow:0 0 20px rgba(0,0,0,0.5);padding:10px;text-align:center;width:640px}
#toolbar{margin-bottom:8px;display:flex;flex-wrap:wrap;justify-content:center;gap:6px}
button,input[type=color],input[type=range]{border:none;border-radius:6px;padding:5px 10px;cursor:pointer;font-size:14px}
button{background:#333;color:#eee}
button.active{background:#6cf;color:#000}
canvas{background:#fff;border-radius:10px;display:block;margin:0 auto}
</style>
</head>
<body>
<div id='drawApp'>
<div id='toolbar'>
<label style='color:#ccc;'>Цвет:</label>
<input type='color' id='colorPick' value='#000000' style='width:60px;height:36px;border:none;outline:none;padding:0;background:none;'>
<label style='color:#ccc;'>Размер:</label>
<input type='range' id='brushSize' min='1' max='80' value='10' style='width:120px'>
<button id='brushBtn' class='active'>Кисть</button>
<button id='softBtn'>Плавная</button>
<button id='eraserBtn'>Ластик</button>
<button id='clearBtn'>Очистить</button>
<button id='saveBtn'>💾 Сохранить</button>
</div>
<canvas id='drawCanvas' width='600' height='600'></canvas>
</div>
<script>
const canvas=document.getElementById('drawCanvas'),ctx=canvas.getContext('2d');
let drawing=false,lastX=0,lastY=0,tool='brush',soft=false;
const colorEl=document.getElementById('colorPick'),sizeEl=document.getElementById('brushSize');
ctx.fillStyle='#fff';ctx.fillRect(0,0,canvas.width,canvas.height);
// 🔄 Загрузка сохранённого рисунка
const saved = localStorage.getItem('rusffDrawing');
if(saved){
const img = new Image();
img.onload = ()=>ctx.drawImage(img,0,0);
img.src = saved;
}
function getPos(e){const r=canvas.getBoundingClientRect();const x=(e.touches?e.touches[0].clientX:e.clientX)-r.left;const y=(e.touches?e.touches[0].clientY:e.clientY)-r.top;return{x,y};}
function start(e){e.preventDefault();drawing=true;const p=getPos(e);lastX=p.x;lastY=p.y;}
function end(){drawing=false;}
function draw(e){if(!drawing)return;e.preventDefault();const p=getPos(e);
if(tool==='eraser'){ctx.globalCompositeOperation='destination-out';ctx.lineWidth=sizeEl.value;ctx.beginPath();ctx.moveTo(lastX,lastY);ctx.lineTo(p.x,p.y);ctx.stroke();}
else if(soft){ctx.globalCompositeOperation='source-over';
const dx=p.x-lastX,dy=p.y-lastY,dist=Math.hypot(dx,dy)||1,steps=Math.ceil(dist/(sizeEl.value/3));
for(let i=0;i<steps;i++){const t=i/steps,x=lastX+dx*t,y=lastY+dy*t,rGrad=sizeEl.value/2;
const g=ctx.createRadialGradient(x,y,0,x,y,rGrad);
const hex=colorEl.value.replace('#','');const r=parseInt(hex.substr(0,2),16),gcol=parseInt(hex.substr(2,2),16),b=parseInt(hex.substr(4,2),16);
g.addColorStop(0,'rgba('+r+','+gcol+','+b+',1)');
g.addColorStop(1,'rgba('+r+','+gcol+','+b+',0)');
ctx.fillStyle=g;ctx.beginPath();ctx.arc(x,y,rGrad,0,Math.PI*2);ctx.fill();}
} else {
ctx.globalCompositeOperation='source-over';
ctx.strokeStyle=colorEl.value;ctx.lineWidth=sizeEl.value;ctx.lineCap=ctx.lineJoin='round';
ctx.beginPath();ctx.moveTo(lastX,lastY);ctx.lineTo(p.x,p.y);ctx.stroke();}
lastX=p.x;lastY=p.y;}
// 🎨 Слушатели
canvas.addEventListener('mousedown',start);canvas.addEventListener('touchstart',start,{passive:false});
canvas.addEventListener('mousemove',draw);canvas.addEventListener('touchmove',draw,{passive:false});
['mouseup','mouseleave','touchend','touchcancel'].forEach(ev=>canvas.addEventListener(ev,end));
document.getElementById('brushBtn').onclick=()=>{tool='brush';soft=false;setActive('brushBtn');};
document.getElementById('softBtn').onclick=()=>{tool='brush';soft=true;setActive('softBtn');};
document.getElementById('eraserBtn').onclick=()=>{tool='eraser';soft=false;setActive('eraserBtn');};
document.getElementById('clearBtn').onclick=()=>{ctx.clearRect(0,0,canvas.width,canvas.height);ctx.fillStyle='#fff';ctx.fillRect(0,0,canvas.width,canvas.height);localStorage.removeItem('rusffDrawing');};
document.getElementById('saveBtn').onclick=()=>{localStorage.setItem('rusffDrawing', canvas.toDataURL());alert('✅ Рисунок сохранён!');};
function setActive(id){['brushBtn','softBtn','eraserBtn'].forEach(i=>document.getElementById(i).classList.remove('active'));document.getElementById(id).classList.add('active');}
// 💾 Автосохранение каждые 10 секунд
setInterval(()=>localStorage.setItem('rusffDrawing', canvas.toDataURL()),10000);
</script>
</body>
</html>
" style="width:650px;height:700px;border:none;border-radius:12px;box-shadow:0 0 20px rgba(0,0,0,0.4)"></iframe>[/html]
Поделиться32025-11-01 22:59:56
[html]<iframe srcdoc="
<html>
<head>
<meta charset='utf-8'>
<style>
body{margin:0;padding:0;background:#111;color:#eee;font-family:Arial,Helvetica,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh}
#drawApp{background:#222;border-radius:10px;box-shadow:0 0 20px rgba(0,0,0,0.5);padding:10px;text-align:center;width:640px}
#toolbar{margin-bottom:1%;display:flex;flex-wrap:wrap;justify-content:center;gap:6px}
button,input[type=color],input[type=range]{border-radius:6px;padding:5px 10px;cursor:pointer;font-size:14px}
button{background:#333;color:#eee}
button.active{background:#6cf;color:#000}
canvas{background:#fff;border-radius:10px;display:block;margin:0 auto}
</style>
</head>
<body>
<div id='drawApp'>
<div id='toolbar'>
<label style='color:#ccc;'>Цвет:</label>
<input type='color' id='colorPick' value='#000000' style='width:60px;height:36px;border:none;outline:none;padding:0;background:none;'>
<label style='color:#ccc;'>Размер:</label>
<input type='range' id='brushSize' min='1' max='80' value='10' style='width:120px'>
<button id='brushBtn' class='active'>Кисть жесткая</button>
<button id='softBtn'>Кисть мягкая</button>
<button id='eraserBtn'>Ластик</button>
<button id='clearBtn'>Очистить холст</button>
<button id='saveBtn'>💾 Сохранить</button>
</div>
<canvas id='drawCanvas' width='600%' height='600%'></canvas>
</div>
<script>
const canvas=document.getElementById('drawCanvas'),ctx=canvas.getContext('2d');
let drawing=false,lastX=0,lastY=0,tool='brush',soft=false;
const colorEl=document.getElementById('colorPick'),sizeEl=document.getElementById('brushSize');
ctx.fillStyle='#fff';ctx.fillRect(0,0,canvas.width,canvas.height);
// 🔄 Загрузка сохранённого рисунка
const saved = localStorage.getItem('rusffDrawing');
if(saved){
const img = new Image();
img.onload = ()=>ctx.drawImage(img,0,0);
img.src = saved;
}
function getPos(e){const r=canvas.getBoundingClientRect();const x=(e.touches?e.touches[0].clientX:e.clientX)-r.left;const y=(e.touches?e.touches[0].clientY:e.clientY)-r.top;return{x,y};}
function start(e){e.preventDefault();drawing=true;const p=getPos(e);lastX=p.x;lastY=p.y;}
function end(){drawing=false;}
function draw(e){if(!drawing)return;e.preventDefault();const p=getPos(e);
if(tool==='eraser'){ctx.globalCompositeOperation='destination-out';ctx.lineWidth=sizeEl.value;ctx.beginPath();ctx.moveTo(lastX,lastY);ctx.lineTo(p.x,p.y);ctx.stroke();}
else if(soft){ctx.globalCompositeOperation='source-over';
const dx=p.x-lastX,dy=p.y-lastY,dist=Math.hypot(dx,dy)||1,steps=Math.ceil(dist/(sizeEl.value/3));
for(let i=0;i<steps;i++){const t=i/steps,x=lastX+dx*t,y=lastY+dy*t,rGrad=sizeEl.value/2;
const g=ctx.createRadialGradient(x,y,0,x,y,rGrad);
const hex=colorEl.value.replace('#','');const r=parseInt(hex.substr(0,2),16),gcol=parseInt(hex.substr(2,2),16),b=parseInt(hex.substr(4,2),16);
g.addColorStop(0,'rgba('+r+','+gcol+','+b+',1)');
g.addColorStop(1,'rgba('+r+','+gcol+','+b+',0)');
ctx.fillStyle=g;ctx.beginPath();ctx.arc(x,y,rGrad,0,Math.PI*2);ctx.fill();}
} else {
ctx.globalCompositeOperation='source-over';
ctx.strokeStyle=colorEl.value;ctx.lineWidth=sizeEl.value;ctx.lineCap=ctx.lineJoin='round';
ctx.beginPath();ctx.moveTo(lastX,lastY);ctx.lineTo(p.x,p.y);ctx.stroke();}
lastX=p.x;lastY=p.y;}
// 🎨 Слушатели
canvas.addEventListener('mousedown',start);canvas.addEventListener('touchstart',start,{passive:false});
canvas.addEventListener('mousemove',draw);canvas.addEventListener('touchmove',draw,{passive:false});
['mouseup','mouseleave','touchend','touchcancel'].forEach(ev=>canvas.addEventListener(ev,end));
document.getElementById('brushBtn').onclick=()=>{tool='brush';soft=false;setActive('brushBtn');};
document.getElementById('softBtn').onclick=()=>{tool='brush';soft=true;setActive('softBtn');};
document.getElementById('eraserBtn').onclick=()=>{tool='eraser';soft=false;setActive('eraserBtn');};
document.getElementById('clearBtn').onclick=()=>{ctx.clearRect(0,0,canvas.width,canvas.height);ctx.fillStyle='#fff';ctx.fillRect(0,0,canvas.width,canvas.height);localStorage.removeItem('rusffDrawing');};
document.getElementById('saveBtn').onclick=()=>{localStorage.setItem('rusffDrawing', canvas.toDataURL());alert('✅ Рисунок сохранён!');};
function setActive(id){['brushBtn','softBtn','eraserBtn'].forEach(i=>document.getElementById(i).classList.remove('active'));document.getElementById(id).classList.add('active');}
// 💾 Автосохранение каждые 10 секунд
setInterval(()=>localStorage.setItem('rusffDrawing', canvas.toDataURL()),10000);
</script>
</body>
</html>
" style="width:100%;height:700px;border:none;border-radius:12px;box-shadow:0 0 20px rgba(0,0,0,0.4)"></iframe>[/html]
Поделиться42025-11-04 22:14:50
[html]<style>
#paintContainer {
position: relative;
display: block;
width: 100%;
max-width: 100%;
}
#overlayImage {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: auto;
pointer-events: none;
user-select: none;
z-index: 2;
}
#paintCanvas {
width: 100%;
height: auto;
border: 1px solid #666;
display: block;
z-index: 1;
}
.toolbar {
margin-top: 10px;
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.palette {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.color {
width: 22px;
height: 22px;
border-radius: 50%;
border: 1px solid #444;
cursor: pointer;
}
button {
padding: 5px 10px;
border-radius: 6px;
border: 1px solid #555;
background: #eee;
cursor: pointer;
}
button:hover { background: #ddd; }
</style>
<div id="paintContainer">
<canvas id="paintCanvas"></canvas>
<img id="overlayImage" src="https://upforme.ru/uploads/001c/84/76/2/729135.png">
</div>
<div class="toolbar">
<div class="palette" id="palette"></div>
<input type="color" id="customColor" value="#000000">
<label>Размер:
<input type="range" id="brushSize" min="5" max="100" value="20">
</label>
<button id="hardBrush">✏️ Жесткая</button>
<button id="softBrush">💨 Мягкая</button>
<button id="eraser">🩹 Ластик</button>
<button id="undo">↩️ Отменить</button>
<button id="saveProgress">💾 Сохранить</button>
<button id="clearCanvas">🧹 Очистить</button>
</div>
<script>
(function() {
const canvas = document.getElementById("paintCanvas");
const ctx = canvas.getContext("2d");
const img = document.getElementById("overlayImage");
let painting = false;
let brushType = "hard";
let currentColor = "#000";
let brushSize = document.getElementById("brushSize");
let erasing = false;
let undoStack = [];
const maxUndo = 20;
function pushUndo() {
if (undoStack.length >= maxUndo) undoStack.shift();
undoStack.push(canvas.toDataURL());
}
// Палитра
const colors = ["#000000","#ffffff","#ff0000","#00ff00","#0000ff","#ffff00","#ff00ff","#00ffff",
"#808080","#804000","#ff8000","#8000ff","#0080ff","#80ff00","#ff0080","#008080",
"#404040","#c0c0c0","#a52a2a","#add8e6"];
const palette = document.getElementById("palette");
colors.forEach(c => {
const el = document.createElement("div");
el.className = "color";
el.style.background = c;
el.onclick = () => { currentColor = c; erasing = false; };
palette.appendChild(el);
});
document.getElementById("customColor").oninput = e => { currentColor = e.target.value; erasing = false; };
// Инструменты
document.getElementById("hardBrush").onclick = () => { brushType = "hard"; erasing = false; };
document.getElementById("softBrush").onclick = () => { brushType = "soft"; erasing = false; };
document.getElementById("eraser").onclick = () => { erasing = true; };
document.getElementById("undo").onclick = () => {
if (!undoStack.length) return;
const last = undoStack.pop();
const imgData = new Image();
imgData.onload = () => ctx.drawImage(imgData, 0, 0, canvas.width, canvas.height);
imgData.src = last;
};
document.getElementById("clearCanvas").onclick = () => {
pushUndo();
ctx.clearRect(0,0,canvas.width,canvas.height);
localStorage.removeItem("coloringProgress");
};
document.getElementById("saveProgress").onclick = () => {
localStorage.setItem("coloringProgress", canvas.toDataURL());
alert("✅ Прогресс сохранён!");
};
// Resize
function resizeCanvas() {
const ratio = img.naturalWidth / img.naturalHeight || 1.5;
canvas.width = img.clientWidth || window.innerWidth;
canvas.height = (img.clientWidth || window.innerWidth) / ratio;
restoreProgress();
}
img.onload = resizeCanvas;
window.onresize = resizeCanvas;
function startDraw(e) {
pushUndo();
painting = true;
draw(e);
}
function endDraw() {
painting = false;
ctx.beginPath();
saveProgress();
}
function draw(e) {
if (!painting) return;
const rect = canvas.getBoundingClientRect();
const x = (e.clientX || e.touches?.[0]?.clientX) - rect.left;
const y = (e.clientY || e.touches?.[0]?.clientY) - rect.top;
if (brushType === "soft") {
softStroke(x, y);
} else if (erasing) {
ctx.globalCompositeOperation = "destination-out";
ctx.beginPath();
ctx.arc(x, y, brushSize.value / 2, 0, Math.PI * 2);
ctx.fill();
} else {
ctx.globalCompositeOperation = "source-over";
ctx.lineWidth = brushSize.value;
ctx.lineCap = "round";
ctx.strokeStyle = currentColor;
ctx.globalAlpha = 1;
ctx.lineTo(x, y);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x, y);
}
}
function softStroke(x, y) {
const radius = brushSize.value / 2;
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// Создаём радиальный градиент аэрографа
const grd = ctx.createRadialGradient(x, y, 0, x, y, radius);
const r = parseInt(currentColor.substr(1,2),16);
const g = parseInt(currentColor.substr(3,2),16);
const b = parseInt(currentColor.substr(5,2),16);
// Полупрозрачный центр, прозрачные края
grd.addColorStop(0, `rgba(${r},${g},${b},0.3)`);
grd.addColorStop(1, 'rgba(0,0,0,0)');
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle = grd;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI*2);
ctx.fill();
}
function saveProgress() {
localStorage.setItem("coloringProgress", canvas.toDataURL());
}
function restoreProgress() {
const saved = localStorage.getItem("coloringProgress");
if (saved) {
const imgData = new Image();
imgData.onload = () => ctx.drawImage(imgData, 0, 0, canvas.width, canvas.height);
imgData.src = saved;
}
}
canvas.addEventListener("mousedown", startDraw);
canvas.addEventListener("mouseup", endDraw);
canvas.addEventListener("mousemove", draw);
canvas.addEventListener("touchstart", startDraw);
canvas.addEventListener("touchend", endDraw);
canvas.addEventListener("touchmove", draw);
})();
</script>[/html]
Поделиться52025-11-19 13:58:27
[html]<style>
/* === Основные стили контейнеров === */
#paintContainer {
position: relative;
display: block;
width: 100%;
max-width: 100%;
}
#overlayImage {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: auto;
pointer-events: none;
user-select: none;
z-index: 2;
}
#paintCanvas {
width: 100%;
height: auto;
border: 1px solid #ccc; /* Более светлая рамка */
display: block;
z-index: 1;
cursor: crosshair; /* Курсор для рисования */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Легкая тень для холста */
border-radius: 8px;
}
/* === Панель инструментов (Toolbar) === */
.toolbar {
margin-top: 20px;
padding: 15px;
background: #f4f4f4; /* Светлый фон */
border-radius: 12px; /* Закругленные углы */
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1); /* Более выраженная тень */
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
font-family: Arial, sans-serif;
}
/* === Палитра === */
.palette {
display: flex;
flex-wrap: wrap;
gap: 6px;
padding: 5px 0;
}
.color {
width: 26px; /* Чуть больше */
height: 26px;
border-radius: 50%;
border: 2px solid transparent; /* Прозрачная рамка для состояния неактивности */
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.color:hover {
transform: scale(1.1);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);
}
.color.active {
border-color: #333; /* Четкая рамка для выбранного цвета */
transform: scale(1.15);
}
/* === Кнопки === */
button {
padding: 8px 15px;
border-radius: 8px;
border: none;
background: #ffffff;
color: #333;
font-weight: 600;
cursor: pointer;
transition: background-color 0.2s, transform 0.1s, box-shadow 0.2s;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 5px;
}
button:hover {
background: #e0e0e0;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
button:active {
transform: scale(0.98);
}
/* === Инпут цвета и Размера === */
#customColor {
width: 30px;
height: 30px;
padding: 0;
border: 2px solid #ccc;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
#customColor::-webkit-color-swatch {
border-radius: 50%;
border: none;
}
#customColor::-moz-color-swatch {
border-radius: 50%;
border: none;
}
.toolbar label {
display: flex;
align-items: center;
gap: 10px;
font-weight: 500;
white-space: nowrap;
}
#brushSize {
width: 120px;
cursor: pointer;
}
</style>
<div id="paintContainer">
<canvas id="paintCanvas"></canvas>
<img id="overlayImage" src="https://upforme.ru/uploads/001c/80/ee/3/30321.png">
</div>
<div class="toolbar">
<div class="palette" id="palette"></div>
<input type="color" id="customColor" value="#000000" title="Выбрать свой цвет">
<label>
Размер:
<input type="range" id="brushSize" min="5" max="100" value="20">
</label>
<button id="hardBrush">✏️ Жесткая</button>
<button id="softBrush">💨 Мягкая</button>
<button id="eraser">🩹 Ластик</button>
<button id="saveProgress">💾 Сохранить</button>
<button id="clearCanvas">🧹 Очистить</button>
</div>
<script>
(function() {
const canvas = document.getElementById("paintCanvas");
const ctx = canvas.getContext("2d");
const img = document.getElementById("overlayImage");
const brushSizeInput = document.getElementById("brushSize");
const customColorInput = document.getElementById("customColor");
let painting = false;
let brushType = "hard";
let currentColor = customColorInput.value;
let erasing = false;
// --- Утилиты для стилизации активных элементов ---
function setActiveColor(targetColor) {
document.querySelectorAll('.color').forEach(el => el.classList.remove('active'));
currentColor = targetColor;
erasing = false;
}
function setActivePaletteColor(el, color) {
setActiveColor(color);
el.classList.add('active');
customColorInput.value = color; // Обновляем инпут цвета
}
// === Палитра ===
const colors = [
"#000000", "#ffffff", "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", "#00ffff",
"#808080", "#804000", "#ff8000", "#8000ff", "#0080ff", "#80ff00", "#ff0080", "#008080",
"#404040", "#c0c0c0", "#a52a2a", "#add8e6"
];
const palette = document.getElementById("palette");
colors.forEach(c => {
const el = document.createElement("div");
el.className = "color";
el.style.backgroundColor = c;
el.onclick = () => setActivePaletteColor(el, c);
palette.appendChild(el);
});
// Изначальная активация первого цвета (черного)
if (palette.firstChild) {
palette.firstChild.classList.add('active');
}
customColorInput.oninput = e => setActiveColor(e.target.value);
// === Настройки ===
document.getElementById("hardBrush").onclick = () => { brushType = "hard"; erasing = false; setActiveColor(customColorInput.value); };
document.getElementById("softBrush").onclick = () => { brushType = "soft"; erasing = false; setActiveColor(customColorInput.value); };
document.getElementById("eraser").onclick = () => { erasing = true; document.querySelectorAll('.color').forEach(el => el.classList.remove('active')); };
document.getElementById("clearCanvas").onclick = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
localStorage.removeItem("coloringProgress");
};
document.getElementById("saveProgress").onclick = () => {
localStorage.setItem("coloringProgress", canvas.toDataURL());
alert("✅ Прогресс сохранён!");
};
// Обработчики кнопок showCode и copyCode удалены.
// === Размер под страницу ===
function resizeCanvas() {
if (img.naturalWidth === 0) return;
const ratio = img.naturalWidth / img.naturalHeight;
const container = document.getElementById("paintContainer");
const clientWidth = container.clientWidth;
canvas.width = clientWidth;
canvas.height = clientWidth / ratio;
restoreProgress();
}
img.onload = resizeCanvas;
window.onresize = resizeCanvas;
if (img.complete) {
resizeCanvas();
}
// === Рисование ===
function startDraw(e) { painting = true; draw(e); }
function endDraw() { painting = false; ctx.beginPath(); saveProgress(); }
function draw(e) {
if (!painting) return;
const rect = canvas.getBoundingClientRect();
const clientX = e.clientX || e.touches?.[0]?.clientX;
const clientY = e.clientY || e.touches?.[0]?.clientY;
if (clientX === undefined || clientY === undefined) return;
const x = clientX - rect.left;
const y = clientY - rect.top;
const brushSize = brushSizeInput.value;
if (brushType === "soft") {
softStroke(x, y, brushSize);
} else if (erasing) {
ctx.globalCompositeOperation = "destination-out";
ctx.beginPath();
ctx.arc(x, y, brushSize / 2, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
} else {
ctx.globalCompositeOperation = "source-over";
ctx.lineWidth = brushSize;
ctx.lineCap = "round";
ctx.strokeStyle = currentColor;
ctx.globalAlpha = 1;
ctx.lineTo(x, y);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x, y);
}
}
// === Мягкая кисть ===
function softStroke(x, y, size) {
const hexToRgb = hex => {
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b);
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? `${parseInt(result[1], 16)},${parseInt(result[2], 16)},${parseInt(result[3], 16)}` : '0,0,0';
};
const rgbColor = hexToRgb(erasing ? '#ffffff' : currentColor);
const grd = ctx.createRadialGradient(x, y, 0, x, y, size / 2);
grd.addColorStop(0, `rgba(${rgbColor}, 0.8)`);
grd.addColorStop(1, `rgba(${rgbColor}, 0)`);
ctx.globalCompositeOperation = erasing ? 'destination-out' : 'source-over';
ctx.globalAlpha = 0.4;
ctx.fillStyle = grd;
ctx.beginPath();
ctx.arc(x, y, size / 2, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
}
// === Сохранение/Восстановление прогресса ===
function saveProgress() {
localStorage.setItem("coloringProgress", canvas.toDataURL());
}
function restoreProgress() {
const saved = localStorage.getItem("coloringProgress");
if (saved) {
const imgData = new Image();
imgData.onload = () => ctx.drawImage(imgData, 0, 0, canvas.width, canvas.height);
imgData.src = saved;
}
}
// === События ===
canvas.addEventListener("mousedown", startDraw);
canvas.addEventListener("mouseup", endDraw);
canvas.addEventListener("mouseout", endDraw);
canvas.addEventListener("mousemove", draw);
canvas.addEventListener("touchstart", startDraw);
canvas.addEventListener("touchend", endDraw);
canvas.addEventListener("touchcancel", endDraw);
canvas.addEventListener("touchmove", draw);
})();
</script>[/html]