Reputation: 41
I am attempting to write a graphical grid editor and I was looking at the possibility of use SVG to draw the grid, with the hope that there is some was to select the grid elements. So, the SVG grid would be made up of colored rectangles arranged in columns and rows. User can draw digital pictures by coloring different rectangles different colors.
I can easily draw a grid of svg rects and display is fine. But I want the user to be able to select a set of rects from the svg display. So, perhaps they want to select multiple rects by dragging a rectangular region with the mouse and selecting them, then they might want to color all a certain color.
Is there a way for the browser to show selection of a subset of rects displayed in my grid? Or is that not a possibility with SVG? I am new to SVG, so have never worked with it before. My simple grid test, does not show any selection when dragging over the svg rect elements with the mouse.
Is there some easy way to do this?
Alternatively, I think I would need to use an HTML 5 canvas for display and handle all the mouse events myself.
Upvotes: 1
Views: 2030
Reputation: 33044
This is how I would do it:
I create the grid and I'm saving the rects in the rects
array.
On mouse down I change the value of the min_x
and min_y
variables.
On mouse up I change the value of the max_x
and max_y
, and filter the rects
array to change the color of the rects in the selected range:
x >= min_x-size &&
y >= min_y-size &&
x <= max_x &&
y <= max_y
This is an example. Please click and drag over the svg canvas.
let SVG_NS = svg.namespaceURI;
let size = 10;// the size of a grid cell
let w = 100;//the width of the grid
let h = 100;//the height of the grid
let rectx=0,recty=0;
let selecting = false;
// the rects array
let rects = [];
//create the grid.Push the new rect into the rects array. All the recta have a fill attribute
for(let y = 0; y< h; y+=size){
for(let x = 0; x < w; x+=size){
let rect = drawSVGelmt({x:x,y:y,width:size,height:size,fill:"white"},"rect", svgG);
rects.push(rect)
}
}
let min_x = 0,max_x=100,min_y=0,max_y = 100
//on mouse down change the value of the min_x and min_y
svg.addEventListener("mousedown",(e)=>{
selecting = true
m = oMousePosSVG(e,svg)
min_x = m.x,min_y=m.y;
rectx = m.x;
recty = m.y;
selector.setAttributeNS(null,"x",rectx);
selector.setAttributeNS(null,"y",recty);
})
//on mouse up change the value of the max_x and max_y, filter the rects array and change the color of the "selected" rects
svg.addEventListener("mousemove",(e)=>{
if(selecting){
m = oMousePosSVG(e,svg);
selector.setAttributeNS(null,"width",m.x-rectx);
selector.setAttributeNS(null,"height",m.y-recty);
}
});
svg.addEventListener("mouseup",(e)=>{
if(selecting){
let m = oMousePosSVG(e,svg)
max_x = m.x,max_y=m.y;
selector.setAttributeNS(null,"x",0);
selector.setAttributeNS(null,"y",0);
selector.setAttributeNS(null,"width",0);
selector.setAttributeNS(null,"height",0);
rects.filter((el)=> {
let x = el.getAttribute("x");
let y = el.getAttribute("y");
if (x >= min_x-size &&
y >= min_y-size &&
x <= max_x &&
y <= max_y){
el.setAttribute("fill","red")}
});
}
selecting = false;
})
// a function to draw a new svg element
function drawSVGelmt(o,tag, parent) {
let elmt = document.createElementNS(SVG_NS, tag);
for (let name in o) {
if (o.hasOwnProperty(name)) {
elmt.setAttributeNS(null, name, o[name]);
}
}
parent.appendChild(elmt);
return elmt;
}
// a function to detect the mouse position on the svg canvas
function oMousePosSVG(e, svg) {
var p = svg.createSVGPoint();
p.x = e.clientX;
p.y = e.clientY;
var ctm = svg.getScreenCTM().inverse();
var p = p.matrixTransform(ctm);
return p;
}
svg{border:1px solid; width:90vh;}
rect{stroke:black; vector-effect:non-scaling-stroke;pointer-events:all}
<svg id="svg" viewBox="0 0 100 100">
<g id="svgG"></g>
<rect id="selector" stroke="#ccc" fill="rgba(0,0,0,.2)" />
</svg>
Upvotes: 5