feat: drawing toolbar is back
This commit is contained in:
parent
459584cb9c
commit
286a0ed371
2 changed files with 57 additions and 16 deletions
|
|
@ -42,35 +42,53 @@ pub fn render_form(config: &Config) -> String {
|
||||||
format!(
|
format!(
|
||||||
r##"<label class="guestbook-label">{label}</label>
|
r##"<label class="guestbook-label">{label}</label>
|
||||||
<canvas class="guestbook-canvas" width="{w}" height="{h}"></canvas>
|
<canvas class="guestbook-canvas" width="{w}" height="{h}"></canvas>
|
||||||
<a href="#" class="guestbook-canvas-reset">Reset</a>
|
<span class="guestbook-canvas-tools"><span class="guestbook-swatch active" data-c="#000" style="background:#000"></span><span class="guestbook-swatch" data-c="#e03131" style="background:#e03131"></span><span class="guestbook-swatch" data-c="#2f9e44" style="background:#2f9e44"></span><span class="guestbook-swatch" data-c="#1971c2" style="background:#1971c2"></span><span class="guestbook-swatch" data-c="#f08c00" style="background:#f08c00"></span><span class="guestbook-swatch" data-c="#9c36b5" style="background:#9c36b5"></span><span class="guestbook-swatch" data-c="#e64980" style="background:#e64980"></span><span class="guestbook-swatch" data-c="eraser" style="background:#fff" title="eraser"></span> <input type="range" class="guestbook-size-slider" min="1" max="20" value="5"> | <a href="#" class="guestbook-undo">undo</a> | <a href="#" class="guestbook-canvas-reset">reset</a></span><input type="hidden" name="drawing"><script>(function(){{
|
||||||
<input type="hidden" name="drawing">
|
|
||||||
<script>
|
|
||||||
(function(){{
|
|
||||||
var c=document.querySelector('.guestbook-canvas'),
|
var c=document.querySelector('.guestbook-canvas'),
|
||||||
x=c.getContext('2d'),
|
x=c.getContext('2d'),
|
||||||
d=false,lx,ly;
|
d=false,lx,ly,h=[],col='#000',sz=5,eraser=false;
|
||||||
|
x.strokeStyle=col;x.lineWidth=sz;x.lineCap='round';x.lineJoin='round';
|
||||||
function pos(e){{var r=c.getBoundingClientRect();
|
function pos(e){{var r=c.getBoundingClientRect();
|
||||||
return[e.clientX-r.left,e.clientY-r.top]}}
|
return[e.clientX-r.left,e.clientY-r.top]}}
|
||||||
function tpos(e){{var r=c.getBoundingClientRect(),t=e.touches[0];
|
function tpos(e){{var r=c.getBoundingClientRect(),t=e.touches[0];
|
||||||
return[t.clientX-r.left,t.clientY-r.top]}}
|
return[t.clientX-r.left,t.clientY-r.top]}}
|
||||||
c.addEventListener('mousedown',function(e){{d=true;var p=pos(e);lx=p[0];ly=p[1]}});
|
function save(){{if(h.length>=20)h.shift();
|
||||||
|
h.push(x.getImageData(0,0,c.width,c.height))}}
|
||||||
|
function dot(px,py){{x.beginPath();x.arc(px,py,sz/2,0,Math.PI*2);
|
||||||
|
if(eraser){{x.save();x.globalCompositeOperation='destination-out';x.fillStyle='#000';x.fill();x.restore()}}
|
||||||
|
else{{x.fillStyle=col;x.fill()}}}}
|
||||||
|
c.addEventListener('mousedown',function(e){{save();d=true;var p=pos(e);lx=p[0];ly=p[1];dot(lx,ly)}});
|
||||||
c.addEventListener('mousemove',function(e){{if(!d)return;var p=pos(e);
|
c.addEventListener('mousemove',function(e){{if(!d)return;var p=pos(e);
|
||||||
x.beginPath();x.moveTo(lx,ly);x.lineTo(p[0],p[1]);x.stroke();lx=p[0];ly=p[1]}});
|
if(eraser)x.globalCompositeOperation='destination-out';
|
||||||
|
x.beginPath();x.moveTo(lx,ly);x.lineTo(p[0],p[1]);x.stroke();
|
||||||
|
if(eraser)x.globalCompositeOperation='source-over';
|
||||||
|
lx=p[0];ly=p[1]}});
|
||||||
c.addEventListener('mouseup',function(){{d=false}});
|
c.addEventListener('mouseup',function(){{d=false}});
|
||||||
c.addEventListener('mouseleave',function(){{d=false}});
|
c.addEventListener('mouseleave',function(){{d=false}});
|
||||||
c.addEventListener('touchstart',function(e){{e.preventDefault();var p=tpos(e);lx=p[0];ly=p[1]}});
|
c.addEventListener('touchstart',function(e){{e.preventDefault();save();var p=tpos(e);lx=p[0];ly=p[1];dot(lx,ly)}});
|
||||||
c.addEventListener('touchmove',function(e){{e.preventDefault();var p=tpos(e);
|
c.addEventListener('touchmove',function(e){{e.preventDefault();var p=tpos(e);
|
||||||
x.beginPath();x.moveTo(lx,ly);x.lineTo(p[0],p[1]);x.stroke();lx=p[0];ly=p[1]}});
|
if(eraser)x.globalCompositeOperation='destination-out';
|
||||||
|
x.beginPath();x.moveTo(lx,ly);x.lineTo(p[0],p[1]);x.stroke();
|
||||||
|
if(eraser)x.globalCompositeOperation='source-over';
|
||||||
|
lx=p[0];ly=p[1]}});
|
||||||
|
var sw=document.querySelectorAll('.guestbook-swatch');
|
||||||
|
sw.forEach(function(s){{s.addEventListener('click',function(){{
|
||||||
|
sw.forEach(function(el){{el.classList.remove('active')}});
|
||||||
|
s.classList.add('active');var c2=s.getAttribute('data-c');
|
||||||
|
if(c2==='eraser'){{eraser=true}}else{{eraser=false;col=c2;x.strokeStyle=col}}
|
||||||
|
}})}});
|
||||||
|
document.querySelector('.guestbook-size-slider').addEventListener('input',function(e){{
|
||||||
|
sz=parseInt(e.target.value);x.lineWidth=sz}});
|
||||||
|
document.querySelector('.guestbook-undo').addEventListener('click',function(e){{
|
||||||
|
e.preventDefault();if(h.length)x.putImageData(h.pop(),0,0)}});
|
||||||
document.querySelector('.guestbook-canvas-reset').addEventListener('click',function(e){{
|
document.querySelector('.guestbook-canvas-reset').addEventListener('click',function(e){{
|
||||||
e.preventDefault();x.clearRect(0,0,c.width,c.height)}});
|
e.preventDefault();h=[];x.clearRect(0,0,c.width,c.height)}});
|
||||||
c.closest('form').addEventListener('submit',function(){{
|
c.closest('form').addEventListener('submit',function(){{
|
||||||
var px=new Uint32Array(x.getImageData(0,0,c.width,c.height).data.buffer);
|
var px=new Uint32Array(x.getImageData(0,0,c.width,c.height).data.buffer);
|
||||||
if(px.some(function(v){{return v!==0}})){{
|
if(px.some(function(v){{return v!==0}})){{
|
||||||
c.closest('form').querySelector('[name=drawing]').value=c.toDataURL('image/png');
|
c.closest('form').querySelector('[name=drawing]').value=c.toDataURL('image/png');
|
||||||
}}
|
}}
|
||||||
}});
|
}});
|
||||||
}})();
|
}})();</script>"##,
|
||||||
</script>"##,
|
|
||||||
label = config.label_drawing,
|
label = config.label_drawing,
|
||||||
w = config.canvas_width,
|
w = config.canvas_width,
|
||||||
h = config.canvas_height,
|
h = config.canvas_height,
|
||||||
|
|
@ -88,8 +106,7 @@ pub fn render_form(config: &Config) -> String {
|
||||||
<label class="guestbook-label">{label_message}</label>
|
<label class="guestbook-label">{label_message}</label>
|
||||||
<textarea class="guestbook-textarea" name="message" style="width:{tw}px;height:{th}px" required></textarea>
|
<textarea class="guestbook-textarea" name="message" style="width:{tw}px;height:{th}px" required></textarea>
|
||||||
{captcha_section}
|
{captcha_section}
|
||||||
{drawing_section}
|
{drawing_section}<input name="url" style="display:none" tabindex="-1" autocomplete="off">
|
||||||
<input name="url" style="display:none" tabindex="-1" autocomplete="off">
|
|
||||||
<button class="guestbook-button" type="submit">{button}</button>
|
<button class="guestbook-button" type="submit">{button}</button>
|
||||||
</form>"#,
|
</form>"#,
|
||||||
prompt = config.form_prompt,
|
prompt = config.form_prompt,
|
||||||
|
|
@ -382,7 +399,7 @@ mod tests {
|
||||||
assert!(form.contains("<canvas"));
|
assert!(form.contains("<canvas"));
|
||||||
assert!(form.contains("class=\"guestbook-canvas\""));
|
assert!(form.contains("class=\"guestbook-canvas\""));
|
||||||
assert!(form.contains("name=\"drawing\""));
|
assert!(form.contains("name=\"drawing\""));
|
||||||
assert!(form.contains("Reset"));
|
assert!(form.contains("reset"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,32 @@
|
||||||
.guestbook-canvas {
|
.guestbook-canvas {
|
||||||
border: 1px solid #000;
|
border: 1px solid #000;
|
||||||
cursor: crosshair;
|
cursor: crosshair;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.guestbook-canvas-tools {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.guestbook-canvas-tools a {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.guestbook-swatch {
|
||||||
|
display: inline-block;
|
||||||
|
width: 0.85em;
|
||||||
|
height: 0.85em;
|
||||||
|
border: 1px solid #000;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0 1px;
|
||||||
|
}
|
||||||
|
.guestbook-swatch.active {
|
||||||
|
border: 1px solid #000;
|
||||||
|
outline: 1px solid #000;
|
||||||
|
}
|
||||||
|
.guestbook-size-slider {
|
||||||
|
width: 4em;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.guestbook-canvas-reset {}
|
|
||||||
.entry-drawing {
|
.entry-drawing {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue