From 5d12d2f8ec632628fbf272b9917d7609b46e69cf Mon Sep 17 00:00:00 2001 From: Ramon Date: Thu, 16 Jul 2026 13:55:36 +0200 Subject: [PATCH] feat: kleinere snapzone, dunnere pennen, potlood met karakter en kalligrafische vulpen (v0.3.75-beta) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Snapzone naast de liniaal/geodriehoek verkleind van 24 naar 10px: vrije halen in de buurt worden niet meer "gegrepen"; op de lat zelf blijft de ruime 24px-zone gelden - Diktekiezer uitgebreid met dun (1 en 2, naast 3/5/8) - ✏️ heet nu Potlood en tekent zo: een zachte, gelijkmatige lijn (~85% dekking) via de overlay-laag, dus égaal ook waar de haal zichzelf kruist - Vulpen schrijft nu echt kalligrafisch met een 45°-penpunt: dik in de neerhaal, dun in de ophaal (gemeten 11px vs 1,5px bij dikte 5); stylusdruk versterkt het effect - Geverifieerd in headless Chromium (9 checks, pixelmetingen van dekking, snapzones en lijndiktes) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF --- VERSION | 2 +- public/js/board.js | 45 ++++++++++++++++++++++++++++----------------- public/js/core.js | 6 +++--- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/VERSION b/VERSION index 28381de..cba424b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.74-beta +0.3.75-beta diff --git a/public/js/board.js b/public/js/board.js index 93c6368..2b777b5 100644 --- a/public/js/board.js +++ b/public/js/board.js @@ -448,7 +448,7 @@ PEN_TOOLS.forEach(([id,icon])=>{ }); DEFAULT_PEN_COLORS.forEach(c=>makeColorButton(c, penbar.querySelector(".pen-colors"))); renderRecentPenColors(); -[3,5,8].forEach(s=>{ +[1,2,3,5,8].forEach(s=>{ const b = document.createElement("button"); b.className = "wp-size"; b.dataset.s = s; const i = document.createElement("i"); @@ -577,7 +577,7 @@ function makeMovable(t){ const px = (e.clientX - r.left) * (board.clientWidth / r.width); const py = (e.clientY - r.top) * (board.clientHeight / r.height); const hit = nearestEdge(px, py); - if(hit && hit.d <= 24){ startInkStroke(e); return; } + if(hit && hit.d <= 24){ startInkStroke(e, 24); return; } } if(e.target.classList.contains("dtool-rot")){ const r = t.getBoundingClientRect(); @@ -951,8 +951,10 @@ function snapPoint(x, y){ } /* start van een haal; ook aanroepbaar vanaf de liniaal/geodriehoek zelf (pen op de randzone van de lat = tekenen, niet verslepen) - de pointer - capture op bInk stuurt de vervolg-events vanzelf hierheen */ -function startInkStroke(e){ + capture op bInk stuurt de vervolg-events vanzelf hierheen. + snapDist: naast de lat een kleine zone (10px, anders "grijpt" de lat + halen die vrij bedoeld zijn), óp de lat zelf ruimer (24px, zie makeMovable). */ +function startInkStroke(e, snapDist = 10){ if(PEN.tool==="text"){ startBoardText(e); return; } bInk.setPointerCapture(e.pointerId); bDraw = true; @@ -962,7 +964,7 @@ function startInkStroke(e){ if(PEN.tool!=="erase" && PEN.tool!=="hand"){ const k = bInk.width / board.clientWidth; const hit = nearestEdge(bx/k, by/k); - if(hit && hit.d <= 24) bSnap = hit; + if(hit && hit.d <= snapDist) bSnap = hit; } [bx,by] = snapPoint(bx, by); bMidX = bMidY = null; @@ -988,7 +990,7 @@ const bStop = ()=>{ donkere overgangen) en de overlay leegmaken */ if(bUsedMark && bMark.width){ bCtx.globalCompositeOperation = "source-over"; - bCtx.globalAlpha = 0.32; + bCtx.globalAlpha = bOverlayAlpha; bCtx.drawImage(bMark, 0, 0); bCtx.globalAlpha = 1; bMarkCtx.clearRect(0, 0, bMark.width, bMark.height); @@ -1003,25 +1005,34 @@ const bStop = ()=>{ }; bInk.addEventListener("pointerup", bStop); bInk.addEventListener("pointercancel", bStop); +/* gereedschappen met transparantie tekenen op de overlay-laag (dekkend) en + worden bij het loslaten in één keer doorgezet - egaal, zonder donkere + overgangen: potlood zacht grafiet-achtig, markeerstift doorschijnend */ +const INK_ALPHA = { pen:0.85, mark:0.32 }; +let bOverlayAlpha = 1; function bStroke(x,y,pressure,ptype){ - const mark = PEN.tool==="mark"; - if(mark) bUsedMark = true; - const ctx = mark ? bMarkCtx : bCtx; /* markeerstift op de eigen laag */ + const layerAlpha = INK_ALPHA[PEN.tool]; + if(layerAlpha){ bUsedMark = true; bOverlayAlpha = layerAlpha; bMark.style.opacity = layerAlpha; } + const ctx = layerAlpha ? bMarkCtx : bCtx; ctx.globalCompositeOperation = PEN.tool==="erase" ? "destination-out" : "source-over"; ctx.globalAlpha = 1; ctx.strokeStyle = PEN.color; let w = PEN.size; if(PEN.tool==="erase") w = PEN.size*6; - else if(mark) w = PEN.size*3.5; + else if(PEN.tool==="mark") w = PEN.size*3.5; else if(PEN.tool==="vulp"){ - let target; - if(ptype==="pen" && pressure > 0){ - target = PEN.size*(0.45 + pressure*1.9); - }else{ - const speed = Math.hypot(x-bx, y-by)/bDpr; - target = Math.max(PEN.size*0.5, Math.min(PEN.size*2.3, PEN.size*2.3 - speed*0.12)); + /* kalligrafie met een 45°-penpunt: dik in de neerhaal (↘/↖ dwars op de + punt), dun in de ophaal (↗/↙ langs de punt) - het klassieke dik-dun + van een echte vulpen. Druk (stylus) versterkt het geheel nog. */ + const dx = x - bx, dy = y - by; + let target = PEN.nib; + if(dx || dy){ + const dir = Math.atan2(dy, dx); + const nib = Math.abs(Math.sin(dir - Math.PI/4)); + target = PEN.size*(0.35 + nib*2.1); } - PEN.nib += (target - PEN.nib)*0.3; + if(ptype==="pen" && pressure > 0) target *= (0.55 + pressure*0.9); + PEN.nib += (target - PEN.nib)*0.35; w = PEN.nib; } ctx.lineWidth = w*bDpr; diff --git a/public/js/core.js b/public/js/core.js index 2d530d9..bbd5e51 100644 --- a/public/js/core.js +++ b/public/js/core.js @@ -2,7 +2,7 @@ "use strict"; /* version — shown until /api/version resolves (or if the fetch fails, e.g. offline). Kept in sync by hand with the VERSION file at the repo root on every release. */ -const VERSION = "0.3.74-beta"; +const VERSION = "0.3.75-beta"; (function(){ const tag = document.getElementById("verTag"); tag.textContent = "v"+VERSION; @@ -39,7 +39,7 @@ const I18N = { bgBlank:"Leeg blad", bgLines:"Lijntjes", bgWrite:"Schrijflijnen", bgGridS:"Kleine rekenblokjes", bgGridB:"Grote rekenblokjes", penSize:"Dikte", penColor:"Kleur", penExtras:"Hulpmiddelen", recentColors:"Recent gebruikt", paletteColors:"Kleuren", customColor:"Eigen kleur", eraser:"Gum", clearAll:"Alles wissen", - toolPen:"Pen", toolFountain:"Vulpen (dun en dik, reageert op druk of snelheid)", + toolPen:"Potlood (zachte, gelijkmatige lijn)", toolFountain:"Vulpen (kalligrafie: dik in de neerhaal, dun in de ophaal)", toolMark:"Markeerstift", toolText:"Tekst typen (klik op het blad)", wMin:"Minimaliseren", wMax:"Herstellen", lvl1:"Niveau 1", lvl2:"Niveau 2", lvl3:"Niveau 3", @@ -313,7 +313,7 @@ const I18N = { bgBlank:"Blank sheet", bgLines:"Lines", bgWrite:"Handwriting lines", bgGridS:"Small maths squares", bgGridB:"Big maths squares", penSize:"Thickness", penColor:"Colour", penExtras:"Tools", recentColors:"Recently used", paletteColors:"Colours", customColor:"Custom colour", eraser:"Eraser", clearAll:"Clear all", - toolPen:"Pen", toolFountain:"Fountain pen (thin and thick, follows pressure or speed)", + toolPen:"Pencil (soft, even line)", toolFountain:"Fountain pen (calligraphy: thick downstroke, thin upstroke)", toolMark:"Highlighter", toolText:"Type text (click the sheet)", wMin:"Minimize", wMax:"Restore", lvl1:"Level 1", lvl2:"Level 2", lvl3:"Level 3",