Numex yayın: public/js/app-paint.js
This commit is contained in:
parent
c8d0062503
commit
1c84188c4c
222
public/js/app-paint.js
Normal file
222
public/js/app-paint.js
Normal file
|
|
@ -0,0 +1,222 @@
|
||||||
|
/* ============================================================
|
||||||
|
AURA OS — Uygulama C: Neon Paint
|
||||||
|
HTML5 Canvas serbest çizim, 4 neon renk, temizle
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
const PaintAppState = {};
|
||||||
|
|
||||||
|
function paintBuildContent() {
|
||||||
|
const tpl = document.getElementById('tpl-paint');
|
||||||
|
if (tpl && tpl.content) {
|
||||||
|
return document.importNode(tpl.content, true).firstElementChild.innerHTML;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
'<div class="paint-app">',
|
||||||
|
' <div class="paint-toolbar">',
|
||||||
|
' <div class="paint-tools">',
|
||||||
|
' <button class="color-btn active" data-color="#00ffcc" style="background:#00ffcc"></button>',
|
||||||
|
' <button class="color-btn" data-color="#ff00ff" style="background:#ff00ff"></button>',
|
||||||
|
' <button class="color-btn" data-color="#39ff14" style="background:#39ff14"></button>',
|
||||||
|
' <button class="color-btn" data-color="#ffaa00" style="background:#ffaa00"></button>',
|
||||||
|
' </div>',
|
||||||
|
' <button class="paint-clear-btn">🧹 Tuvali Temizle</button>',
|
||||||
|
' </div>',
|
||||||
|
' <div class="paint-canvas-wrap"><canvas class="paint-canvas" width="760" height="440"></canvas></div>',
|
||||||
|
'</div>'
|
||||||
|
].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Canvas çizim motoru */
|
||||||
|
function initPaintCanvas(bodyEl, canvas) {
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Neon efekt için ikinci "glow" offscreen canvas
|
||||||
|
const glowCanvas = document.createElement('canvas');
|
||||||
|
glowCanvas.width = canvas.width;
|
||||||
|
glowCanvas.height = canvas.height;
|
||||||
|
const glowCtx = glowCanvas.getContext('2d');
|
||||||
|
glowCanvas.style.display = 'none';
|
||||||
|
bodyEl.appendChild(glowCanvas);
|
||||||
|
|
||||||
|
// Durum
|
||||||
|
const state = {
|
||||||
|
drawing: false,
|
||||||
|
color: '#00ffcc',
|
||||||
|
lastX: 0,
|
||||||
|
lastY: 0,
|
||||||
|
lineWidth: 4,
|
||||||
|
erasing: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Canvas içindeki noktayı hesapla (konumlandırma)
|
||||||
|
function getPos(e) {
|
||||||
|
const r = canvas.getBoundingClientRect();
|
||||||
|
const cx = canvas.width / r.width; // ölçekleme
|
||||||
|
const cy = canvas.height / r.height;
|
||||||
|
let clientX, clientY;
|
||||||
|
if (e.touches && e.touches.length) {
|
||||||
|
clientX = e.touches[0].clientX;
|
||||||
|
clientY = e.touches[0].clientY;
|
||||||
|
} else {
|
||||||
|
clientX = e.clientX;
|
||||||
|
clientY = e.clientY;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
x: (clientX - r.left) * cx,
|
||||||
|
y: (clientY - r.top) * cy,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Neon ışık saçılımı: çizgiyi birden çok katman çiziyoruz */
|
||||||
|
function neonStroke(x0, y0, x1, y1, color, width, erase) {
|
||||||
|
if (erase) {
|
||||||
|
// Silgi modu: ikisini de sil (normal ctx)
|
||||||
|
ctx.save();
|
||||||
|
ctx.globalCompositeOperation = 'destination-out';
|
||||||
|
ctx.strokeStyle = 'rgba(0,0,0,1)';
|
||||||
|
ctx.lineWidth = width;
|
||||||
|
ctx.lineCap = 'round';
|
||||||
|
ctx.lineJoin = 'round';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x0, y0);
|
||||||
|
ctx.lineTo(x1, y1);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.restore();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1) Glow offscreen katmanı (geniş + düşük opaklık, blur ile bakılır)
|
||||||
|
glowCtx.save();
|
||||||
|
glowCtx.globalCompositeOperation = 'lighter';
|
||||||
|
glowCtx.strokeStyle = color;
|
||||||
|
glowCtx.shadowColor = color;
|
||||||
|
glowCtx.shadowBlur = 18;
|
||||||
|
glowCtx.lineWidth = width * 2.2;
|
||||||
|
glowCtx.lineCap = 'round';
|
||||||
|
glowCtx.lineJoin = 'round';
|
||||||
|
glowCtx.beginPath();
|
||||||
|
glowCtx.moveTo(x0, y0);
|
||||||
|
glowCtx.lineTo(x1, y1);
|
||||||
|
glowCtx.stroke();
|
||||||
|
glowCtx.restore();
|
||||||
|
|
||||||
|
// 2) Ana çizgiyi koyu neon tonda
|
||||||
|
glowCtx.save();
|
||||||
|
glowCtx.strokeStyle = color;
|
||||||
|
glowCtx.shadowBlur = 6;
|
||||||
|
glowCtx.lineWidth = width;
|
||||||
|
glowCtx.lineCap = 'round';
|
||||||
|
glowCtx.beginPath();
|
||||||
|
glowCtx.moveTo(x0, y0);
|
||||||
|
glowCtx.lineTo(x1, y1);
|
||||||
|
glowCtx.stroke();
|
||||||
|
glowCtx.restore();
|
||||||
|
|
||||||
|
// 3) Parlak merkez
|
||||||
|
glowCtx.save();
|
||||||
|
glowCtx.globalCompositeOperation = 'lighter';
|
||||||
|
glowCtx.strokeStyle = 'rgba(255,255,255,0.7)';
|
||||||
|
glowCtx.lineWidth = Math.max(1, width * 0.35);
|
||||||
|
glowCtx.lineCap = 'round';
|
||||||
|
glowCtx.beginPath();
|
||||||
|
glowCtx.moveTo(x0, y0);
|
||||||
|
glowCtx.lineTo(x1, y1);
|
||||||
|
glowCtx.stroke();
|
||||||
|
glowCtx.restore();
|
||||||
|
|
||||||
|
// 4) Glow canvas'ı ana canvas'a aktar
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.drawImage(glowCanvas, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Olaylar ---- */
|
||||||
|
function pointerDown(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const p = getPos(e);
|
||||||
|
state.drawing = true;
|
||||||
|
state.lastX = p.x;
|
||||||
|
state.lastY = p.y;
|
||||||
|
// nokta
|
||||||
|
neonStroke(p.x, p.y, p.x + 0.2, p.y + 0.2, state.color, state.lineWidth, state.erasing);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pointerMove(e) {
|
||||||
|
if (!state.drawing) return;
|
||||||
|
e.preventDefault();
|
||||||
|
const p = getPos(e);
|
||||||
|
neonStroke(state.lastX, state.lastY, p.x, p.y, state.color, state.lineWidth, state.erasing);
|
||||||
|
state.lastX = p.x;
|
||||||
|
state.lastY = p.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pointerEnd(e) {
|
||||||
|
state.drawing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fare
|
||||||
|
canvas.addEventListener('mousedown', pointerDown);
|
||||||
|
window.addEventListener('mousemove', pointerMove);
|
||||||
|
window.addEventListener('mouseup', pointerEnd);
|
||||||
|
|
||||||
|
// Dokunmatik
|
||||||
|
canvas.addEventListener('touchstart', pointerDown, { passive: false });
|
||||||
|
canvas.addEventListener('touchmove', pointerMove, { passive: false });
|
||||||
|
canvas.addEventListener('touchend', pointerEnd);
|
||||||
|
|
||||||
|
// Sağ lob fare: silgi modu (contextmenu bastır)
|
||||||
|
canvas.addEventListener('contextmenu', (e) => e.preventDefault());
|
||||||
|
|
||||||
|
// Renk seçim butonları
|
||||||
|
const colorBtns = bodyEl.querySelectorAll('.color-btn');
|
||||||
|
colorBtns.forEach(btn => {
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
state.color = btn.dataset.color;
|
||||||
|
state.erasing = false;
|
||||||
|
colorBtns.forEach(b => b.classList.remove('active'));
|
||||||
|
btn.classList.add('active');
|
||||||
|
// canvas imleci rengi
|
||||||
|
canvas.style.cursor = 'crosshair';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Temizle butonu
|
||||||
|
const clearBtn = bodyEl.querySelector('.paint-clear-btn');
|
||||||
|
if (clearBtn) {
|
||||||
|
clearBtn.addEventListener('click', () => {
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
glowCtx.clearRect(0, 0, glowCanvas.width, glowCanvas.height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial draw: boş arka planı
|
||||||
|
ctx.fillStyle = '#05060f';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
glowCtx.fillStyle = '#05060f';
|
||||||
|
glowCtx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Shift silgiyi keskinleştir — imleç css
|
||||||
|
state.color = '#00ffcc';
|
||||||
|
canvas.style.cursor = 'crosshair';
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerApp('paint', {
|
||||||
|
name: 'Neon Paint',
|
||||||
|
icon: '🎨',
|
||||||
|
defaultWidth: 780,
|
||||||
|
defaultHeight: 520,
|
||||||
|
windowTitle: 'Neon Paint — Aura OS',
|
||||||
|
windowProps: { width: 780, height: 520 },
|
||||||
|
|
||||||
|
buildContent: paintBuildContent,
|
||||||
|
|
||||||
|
onLaunch(record, bodyEl) {
|
||||||
|
bodyEl.innerHTML = paintBuildContent();
|
||||||
|
|
||||||
|
const canvas = bodyEl.querySelector('.paint-canvas');
|
||||||
|
if (!canvas) return;
|
||||||
|
|
||||||
|
initPaintCanvas(bodyEl, canvas);
|
||||||
|
},
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user