Browse Source

More betterer active image highlight

ssao
A.Olokhtonov 5 months ago
parent
commit
66e84c1a50
  1. 3
      README.txt
  2. 4
      client/default.css
  3. 52
      client/icons/pointer.svg
  4. 1
      client/index.html
  5. 2
      client/index.js
  6. 13
      client/tools.js
  7. 14
      client/webgl_draw.js
  8. 42
      client/webgl_listeners.js
  9. 13
      client/webgl_shaders.js

3
README.txt

@ -30,6 +30,9 @@ Release:
- Do NOT use session id as player id LUL - Do NOT use session id as player id LUL
- Save events to indexeddb (as some kind of a blob), restore on reconnect and page reload - Save events to indexeddb (as some kind of a blob), restore on reconnect and page reload
- Local prediction for tools! - Local prediction for tools!
- Immediately commit a stroke to the canvas, change order if earlier strokes arrive
- Show my own image immediately, show placeholders while images are loading (add bitmap size to event)
- undo immediately, this one can not arrive out of order, because noone else is going to undo MY actions
* Missing features I do not consider bonus * Missing features I do not consider bonus
+ Player pointers + Player pointers
+ Pretty player pointers + Pretty player pointers

4
client/default.css

@ -43,6 +43,10 @@ canvas {
cursor: url('icons/crosshair.svg') 16 16, crosshair; cursor: url('icons/crosshair.svg') 16 16, crosshair;
} }
canvas.tool-pointer {
cursor: default;
}
canvas.picker { canvas.picker {
cursor: url('icons/picker.svg') 0 19, crosshair; cursor: url('icons/picker.svg') 0 19, crosshair;
} }

52
client/icons/pointer.svg

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32"
height="32"
viewBox="0 0 8.4666665 8.4666666"
version="1.1"
id="svg5"
sodipodi:docname="pointer.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="13.455443"
inkscape:cx="4.2733635"
inkscape:cy="26.086097"
inkscape:window-width="2558"
inkscape:window-height="1412"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid1775" />
</sodipodi:namedview>
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="M 2.5420359,7.0728549 2.0552699,0.9380846 7.1247537,4.4270217 5.0270833,5.0270833 6.0854165,6.8601704 5.1688732,7.3893369 4.1105397,5.5562497 Z"
id="path1887"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

1
client/index.html

@ -234,6 +234,7 @@
<div class="tools-wrapper"> <div class="tools-wrapper">
<div class="tools"> <div class="tools">
<div class="tool" data-tool="pointer"><img draggable="false" src="icons/pointer.svg"></div>
<div class="tool active" data-tool="pencil"><img draggable="false" src="icons/pen.svg"></div> <div class="tool active" data-tool="pencil"><img draggable="false" src="icons/pen.svg"></div>
<div class="tool" data-tool="ruler"><img draggable="false" src="icons/ruler.svg"></div> <div class="tool" data-tool="ruler"><img draggable="false" src="icons/ruler.svg"></div>
<div class="tool" data-tool="eraser"><img draggable="false" src="icons/erase.svg"></div> <div class="tool" data-tool="eraser"><img draggable="false" src="icons/erase.svg"></div>

2
client/index.js

@ -233,7 +233,7 @@ async function main() {
'wasm': {}, 'wasm': {},
'background_pattern': 'grid', 'background_pattern': 'dots',
}; };
const context = { const context = {

13
client/tools.js

@ -10,9 +10,22 @@ function switch_tool(state, item) {
state.tools.active_element.classList.remove('active'); state.tools.active_element.classList.remove('active');
} }
const old_class = 'tool-' + state.tools.active;
const new_class = 'tool-' + tool;
document.querySelector('canvas').classList.remove(old_class);
state.tools.active = tool; state.tools.active = tool;
state.tools.active_element = item; state.tools.active_element = item;
state.tools.active_element.classList.add('active'); state.tools.active_element.classList.add('active');
document.querySelector('canvas').classList.add(new_class);
if (tool === 'pointer' || tool === 'eraser') {
document.querySelector('.brush-dom').classList.add('dhide');
} else {
document.querySelector('.brush-dom').classList.remove('dhide');
}
} }
function select_color(state, item, color_u32) { function select_color(state, item, color_u32) {

14
client/webgl_draw.js

@ -226,20 +226,22 @@ async function draw(state, context, animate, ts) {
gl.vertexAttribPointer(locations['a_pos'], 2, gl.FLOAT, false, 2 * 4, 0); gl.vertexAttribPointer(locations['a_pos'], 2, gl.FLOAT, false, 2 * 4, 0);
for (const entry of context.images) { for (const entry of context.images) {
if (state.active_image === entry.key) {
//gl.uniform1i(locations['u_active'], 1);
} else {
//gl.uniform1i(locations['u_active'], 0);
}
gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height); gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height);
gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom); gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom);
gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y); gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y);
gl.uniform1i(locations['u_texture'], 0); // Only 1 active texture for each drawcall gl.uniform1i(locations['u_texture'], 0); // Only 1 active texture for each drawcall
gl.uniform1i(locations['u_solid'], 0);
gl.bindTexture(gl.TEXTURE_2D, entry.texture); gl.bindTexture(gl.TEXTURE_2D, entry.texture);
gl.drawArrays(gl.TRIANGLES, offset, 6); gl.drawArrays(gl.TRIANGLES, offset, 6);
// Highlight active image
if (entry.key === context.active_image) {
gl.uniform1i(locations['u_solid'], 1);
gl.uniform4f(locations['u_color'], 0.133 * 0.5, 0.545 * 0.5, 0.902 * 0.5, 0.5);
gl.drawArrays(gl.TRIANGLES, offset, 6);
}
offset += 6; offset += 6;
} }
} }

42
client/webgl_listeners.js

@ -107,6 +107,7 @@ function zenmode() {
} }
function enter_picker_mode(state, context) { function enter_picker_mode(state, context) {
if (state.tools.active === 'pencil') { // or other drawing tools
document.querySelector('canvas').classList.add('picker'); document.querySelector('canvas').classList.add('picker');
document.querySelector('.picker-preview-outer').classList.remove('dhide'); document.querySelector('.picker-preview-outer').classList.remove('dhide');
document.querySelector('.brush-dom').classList.add('dhide'); document.querySelector('.brush-dom').classList.add('dhide');
@ -116,13 +117,16 @@ function enter_picker_mode(state, context) {
const canvasp = screen_to_canvas(state, state.cursor); const canvasp = screen_to_canvas(state, state.cursor);
update_color_picker_color(state, context, canvasp); update_color_picker_color(state, context, canvasp);
} }
}
function exit_picker_mode(state) { function exit_picker_mode(state) {
if (state.colorpicking) {
document.querySelector('canvas').classList.remove('picker'); document.querySelector('canvas').classList.remove('picker');
document.querySelector('.picker-preview-outer').classList.add('dhide'); document.querySelector('.picker-preview-outer').classList.add('dhide');
document.querySelector('.brush-dom').classList.remove('dhide'); document.querySelector('.brush-dom').classList.remove('dhide');
state.colorpicking = false; state.colorpicking = false;
} }
}
async function paste(e, state, context) { async function paste(e, state, context) {
const items = (e.clipboardData || e.originalEvent.clipboardData).items; const items = (e.clipboardData || e.originalEvent.clipboardData).items;
@ -160,7 +164,7 @@ function keyup(e, state, context) {
state.moving = false; state.moving = false;
context.canvas.classList.remove('movemode'); context.canvas.classList.remove('movemode');
} else if (e.code === 'ControlLeft' || e.code === 'ControlRight') { } else if (e.code === 'ControlLeft' || e.code === 'ControlRight') {
exit_picker_mode(state); exit_picker_mode(state);exit_picker_mode
} else if (e.code === 'KeyZ') { } else if (e.code === 'KeyZ') {
state.zoomdown = false; state.zoomdown = false;
} }
@ -170,21 +174,6 @@ function mousedown(e, state, context) {
const screenp = {'x': window.devicePixelRatio * e.clientX, 'y': window.devicePixelRatio * e.clientY}; const screenp = {'x': window.devicePixelRatio * e.clientX, 'y': window.devicePixelRatio * e.clientY};
const canvasp = screen_to_canvas(state, screenp); const canvasp = screen_to_canvas(state, screenp);
if (e.button === 2) {
// Right click on image to select it
const image_event = image_at(state, canvasp.x, canvasp.y);
if (image_event) {
context.active_image = image_event.image_id;
} else {
context.active_image = null;
}
schedule_draw(state, context);
return;
}
if (e.button !== 0 && e.button !== 1) { if (e.button !== 0 && e.button !== 1) {
return; return;
} }
@ -203,15 +192,6 @@ function mousedown(e, state, context) {
return; return;
} }
if (context.active_image) {
// Move selected image with left click
const image_event = image_at(state, canvasp.x, canvasp.y);
if (image_event && image_event.image_id === context.active_image) {
state.moving_image = image_event;
return;
}
}
if (state.spacedown || e.button === 1) { if (state.spacedown || e.button === 1) {
state.moving = true; state.moving = true;
context.canvas.classList.add('moving'); context.canvas.classList.add('moving');
@ -236,6 +216,16 @@ function mousedown(e, state, context) {
} else if (state.tools.active === 'eraser') { } else if (state.tools.active === 'eraser') {
state.erasing = true; state.erasing = true;
} else if (state.tools.active === 'pointer') {
const image_event = image_at(state, canvasp.x, canvasp.y);
if (image_event) {
context.active_image = image_event.image_id;
} else {
context.active_image = null;
}
schedule_draw(state, context);
} }
} }
@ -412,7 +402,7 @@ function mouseleave(e, state, context) {
context.canvas.classList.remove('movemode'); context.canvas.classList.remove('movemode');
} }
exit_picker_mode(state); //exit_picker_mode(state);
// something else? // something else?
} }

13
client/webgl_shaders.js

@ -207,8 +207,6 @@ const tquad_vs_src = `#version 300 es
void main() { void main() {
vec2 screen01 = (a_pos * u_scale + u_translation) / u_res; vec2 screen01 = (a_pos * u_scale + u_translation) / u_res;
vec2 screen02 = screen01 * 2.0; vec2 screen02 = screen01 * 2.0;
screen02.y = 2.0 - screen02.y;
vec2 screen11 = screen02 - 1.0;
int vertex_index = gl_VertexID % 6; int vertex_index = gl_VertexID % 6;
@ -222,6 +220,9 @@ const tquad_vs_src = `#version 300 es
v_texcoord = vec2(1.0, 1.0); v_texcoord = vec2(1.0, 1.0);
} }
screen02.y = 2.0 - screen02.y;
vec2 screen11 = screen02 - 1.0;
gl_Position = vec4(screen11, 0, 1); gl_Position = vec4(screen11, 0, 1);
} }
`; `;
@ -232,11 +233,17 @@ const tquad_fs_src = `#version 300 es
in vec2 v_texcoord; in vec2 v_texcoord;
uniform sampler2D u_texture; uniform sampler2D u_texture;
uniform int u_solid;
uniform vec4 u_color;
layout(location = 0) out vec4 FragColor; layout(location = 0) out vec4 FragColor;
void main() { void main() {
if (u_solid == 0) {
FragColor = texture(u_texture, v_texcoord); FragColor = texture(u_texture, v_texcoord);
} else {
FragColor = u_color;
}
} }
`; `;
@ -405,6 +412,8 @@ function init_webgl(state, context) {
'u_scale': gl.getUniformLocation(context.programs['image'], 'u_scale'), 'u_scale': gl.getUniformLocation(context.programs['image'], 'u_scale'),
'u_translation': gl.getUniformLocation(context.programs['image'], 'u_translation'), 'u_translation': gl.getUniformLocation(context.programs['image'], 'u_translation'),
'u_texture': gl.getUniformLocation(context.programs['image'], 'u_texture'), 'u_texture': gl.getUniformLocation(context.programs['image'], 'u_texture'),
'u_solid': gl.getUniformLocation(context.programs['image'], 'u_solid'),
'u_color': gl.getUniformLocation(context.programs['image'], 'u_color'),
}; };
context.locations['debug'] = { context.locations['debug'] = {

Loading…
Cancel
Save