Why is no shape created? - javascript

I am trying to learn SVG and have the following code. The point clicked is printed ok, but no shape is created. Can someone plese point out what I am doing wrong.
<?xml version='1.0' standalone='no'?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 20001102//EN' 'http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd'>
<svg width='100%' height='100%' xmlns='http://www.w3.org/2000/svg' onload='Init(evt)' onmousedown='Grab(evt)' >
<title>Drag And Drop</title>
<script><![CDATA[
var SVGDocument = null;
var SVGRoot = null;
var BackDrop = null;
function Init(evt)
{
SVGDocument = evt.target.ownerDocument;
SVGRoot = SVGDocument.documentElement;
BackDrop = SVGDocument.getElementById('BackDrop');
}
function Grab(evt)
{
var targetElement = evt.target;
if ( BackDrop == targetElement )
{
alert ( 'point: ' + evt.clientX + ' ' + evt.clientY);
var c1 = SVGDocument.createElementNS("http://www.w3.org/2000/svg", "circle");
c1.setAttribute("cx", evt.clientX);
c1.setAttribute("cy", evt.clientY);
c1.setAttribute("r", "100");
c1.setAttribute("fill", "#336699");
BackDrop.appendChild(c1);
}
};
]]></script>
<rect id='BackDrop' x='-10%' y='-10%' width='110%' height='110%' fill='none' pointer-events='all' />
</svg>

BackDrop is a <rect> and <rect> elements are not containers that can have graphic element children. If you create the circles as children of the root element instead i.e. change
BackDrop.appendChild(c1);
to
SVGRoot.appendChild(c1);
the circles will appear.

Related

Programmatically change SVG classes during runtime

-I want to draw the same SVG onto a canvas multiple times, but each time I want to PROGRAMMATICALLY change the colors of specific classes within that SVG.
For example take this house image below:
The SVG for this House has the following classes:
<style>
.window-class {
fill: lime;
}
.door-class {
fill: blue;
}
.house-class {
fill: tan;
}
.roof-class {
fill: red;
}
</style>
How do I programmatically access these specific Style-Classes so I can change their color values for each new house that I draw?
I’m constructing the SVG by creating an Image object and then drawing that image onto a canvas using the following code:
// 1. Create the CANVAS and the CONTEXT:
var theCanvas = document.getElementById("theCanvas");
var theContext = theCanvas.getContext("2d");
theContext.fillStyle = "lightblue";
theContext.fillRect(0, 0, theCanvas.width, theCanvas.height);
// 2. Define the SVG data:
var imageData = '<svg id="HOUSE" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="240.26" height="311.24" viewBox="0 0 240.26 311.24"><defs><style>.house-class {fill: tan;}.roof-class {fill: red;}.roof-class, .window-class, .door-class {stroke: #000;stroke-miterlimit: 10;}.window-class {fill: lime;}.door-class {fill: blue;}</style></defs><g id="House"><rect class="house-class" x="30.08" y="131.74" width="173.07" height="179"/><path d="M270,242V420H98V242H270m1-1H97V421H271V241Z" transform="translate(-67.39 -109.76)"/></g><polygon id="Roof" class="roof-class" points="1.11 131.74 239.11 131.74 117.11 0.74 1.11 131.74"/><rect id="Window2" class="window-class" x="145.11" y="160.74" width="30" height="42"/><rect id="Window1" class="window-class" x="58.61" y="160.74" width="30" height="42"/><rect id="Door" class="door-class" x="92.11" y="228.74" width="52" height="82"/></svg>';
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svg = new Blob([imageData], { type: 'image/svg+xml;charset=utf-8' });
var url = DOMURL.createObjectURL(svg);
img.onload = function () {
theContext.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
}
img.src = url;
Ordinarily I'd be able to get at the specific Classes who's colors I want to change by using this:
let nodeList = document.getElementsByClassName("window-class");
And then I would iterate through that nodeList and at each element I find that is styled with this window-class, I would do:
element.style.fill = -whatever-the-next-color-would-be-;
But since I'm creating my image in the manner I showed above, I'm not sure how I can get at specific classes of its SVG.
Any thoughts?
==============================
UPDATE:
It was pointed out that the code for drawing the image/SVG multiple times wasn't included, so here it is:
// GLOBAL VARIABLES:
const TOTAL_IMAGES = 3; // could be 30, or 300
const canvasWidth = 250;
const canvasHeight = 320;
var canvasX, canvasY = 0;
// COLOR VARIABLES:
var colorCounter = 0;
let houseColorsArray = ["fuchsia", "gold", "lighblue"]; // Will have lots more colors for all of these
let windowColorsArray = ["yellow", "pink", "lightgreen"];
let roofColorsArray = ["maroon", "crimson", "darkred"];
let doorColorsArray = ["darkBlue", "purple", "darkslategray"];
// CLASS-NAMES
let classNamesToPaintArray = [".house-class", ".door-class", ".window-class", ".roof-class"];
function designOneHouse(theCanvas) {
console.log("\n\n==========================\n=");
console.log("= =>>In 'designOneHouse()'!\n");
// 1. Create a Color-Scheme:
let houseColor = houseColorsArray[colorCounter];
let doorColor = doorColorsArray[colorCounter];
let windowColor = windowColorsArray[colorCounter];
let roofColor = roofColorsArray[colorCounter];
console.log(" ->Current 'houseColor' = ", houseColor);
console.log(" ->Current 'doorColor' = ", doorColor);
console.log(" ->Current 'windowColor' = ", windowColor);
console.log(" ->Current 'roofColor' = ", roofColor);
let context = theCanvas.getContext("2d");
// Iterate the ColorCounter - making sure we don't overflow the ColorsArrays:
colorCounter++;
if(colorCounter == houseColorsArray.length) {
colorCounter = 0;
}
// Now GET-AT and PAINT the Individual SVG Components.
// STRATEGY:
// 1. Iterate through the Array containing all the CLASS-NAMES who's color I want to change.
// 2. For each of these classes, I'll need to iterate through all the HTML elements that are OF that class type
// (there may be like 10 elements that are all styled by the same Style; I want all of them to be updated!)
//
for(classNameCounter = 0; classNameCounter < classNamesToPaintArray.length; classNameCounter++) {
let currentClassName = classNamesToPaintArray[classNameCounter];
console.log("currentClassName = " + currentClassName);
let nodeList = document.getElementsByClassName(currentClassName);
console.log("nodeList = " + nodeList);
console.log("nodeList LENGTH = " + nodeList.length);
for(var counter = 0; counter < nodeList.length; counter++) {
console.log("\n\n===>>IN FOR LOOP -- Node-COUNTER = " + counter);
let currentNode = nodeList[counter];
console.dir(" > 'childNodes[0]' of 'currentNode' = ");
console.dir(currentNode.childNodes[0]);
let elements = document.querySelectorAll(".door-class");
// Change the text of multiple elements with a loop
elements.forEach(element => {
element.style.fill = "pink";
});
}
}
}
function makeCanvasGrid() {
console.log("\n\n====>In 'makeCanvasGrid()'!\n");
for(var canvasCounter = 0; canvasCounter < TOTAL_IMAGES; canvasCounter++) {
console.log("\n >FOR LOOP - canvasCounter = " + canvasCounter);
// 1. Create a new Canvas Object:
let newCanvas = document.createElement("canvas");
newCanvas.setAttribute("width", canvasWidth);
newCanvas.setAttribute("height", canvasHeight);
newCanvas.setAttribute("id", "newCanvas" + canvasCounter);
// Log-out just to verify the "id" property was set correctly:
console.log(" >newCanvas.id = " + newCanvas.id);
// 2. Place the Canvas at (x,y) (top, left) coordinates:
newCanvas.style.position = "absolute";
newCanvas.style.left = canvasX; //"100px";
newCanvas.style.top = canvasY; //"100px";
designOneHouse(newCanvas);
// Check the current Canvas' (X, Y) coords, and if needed, reset X to 0 and SKIP to the next "ROW" of Canvasses:
if(canvasCounter > 0 && canvasCounter % 3 == 0) {
console.log(" >>NEXT ROW PLEASE!!!! canvasCount = ", canvasCounter);
canvasX = 0;
canvasY += canvasHeight + 20;
}
else {
canvasX += canvasWidth + 10;
}
}
}
makeCanvasGrid();
SO when I run this right now, the console shows me the nodeList is empty:
nodeList LENGTH = 0
So basically this statement isn't working:
let nodeList = document.getElementsByClassName(currentClassName);
To manipulate the house's DOM, the SVG has to be in the DOM. So I've wrapped the SVG in a <div> and hidden the div. I've put it way offscreen, but I could have hidden the div in several other ways.
Once you do that, your next problem is that you are changing the fill of the elements, but that will be overridded by the CSS in your SVG. So you have to remove those CSS styles.
Thirdly, you are creating canvas objects, but you are not attaching them to the DOM.
Also you are getting an error because canvasX isn't initialised. Plus CSS lengths must have units. So you need newCanvas.style.left = canvasX + "px" etc.
You were also looking up your elements wrongly. getElementsByClassName(".hose-class") won't find anything. It needed to be getElementsByClassName(".hose-class").
Finally, I've rewritten the element lookup and colour assignment code. I've bundled each colour scheme up into an array of colour scheme objects. It makes mapping classes to colours much simpler.
// GLOBAL VARIABLES:
const TOTAL_IMAGES = 3; // could be 30, or 300
const canvasWidth = 250;
const canvasHeight = 320;
var canvasX = 0, canvasY = 0;
// COLOR VARIABLES:
var colorCounter = 0;
let houseColorSchemes = [ {".house-class": "fuchsia",
".door-class": "darkblue",
".window-class": "yellow",
".roof-class": "maroon"},
{".house-class": "gold",
".door-class": "purple",
".window-class": "pink",
".roof-class": "crimson"},
{".house-class": "lightblue",
".door-class": "darkslategray",
".window-class": "lightgreen",
".roof-class": "darkred"} ];
// CLASS-NAMES
let classNamesToPaintArray = [".house-class", ".door-class", ".window-class", ".roof-class"];
// SVG template
let houseSVG = document.getElementById("HOUSE");
function designOneHouse(theCanvas) {
console.log("\n\n==========================\n=");
console.log("= =>>In 'designOneHouse()'!\n");
let context = theCanvas.getContext("2d");
// Now GET-AT and PAINT the Individual SVG Components.
// STRATEGY:
// 1. Iterate through the Array containing all the CLASS-NAMES who's color I want to change.
// 2. For each of these classes, I'll need to iterate through all the HTML elements that are OF that class type
// (there may be like 10 elements that are all styled by the same Style; I want all of them to be updated!)
//
let colorScheme = houseColorSchemes[colorCounter];
classNamesToPaintArray.forEach(className => {
let elements = houseSVG.querySelectorAll(className);
elements.forEach(element => element.style.fill = colorScheme[className]);
});
var imageData = houseSVG.outerHTML;
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svg = new Blob([imageData], { type: 'image/svg+xml;charset=utf-8' });
var url = DOMURL.createObjectURL(svg);
img.onload = function () {
context.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
}
img.src = url;
// Iterate the ColorCounter - making sure we don't overflow the ColorsArrays:
colorCounter++;
if(colorCounter == houseColorSchemes.length) {
colorCounter = 0;
}
}
function makeCanvasGrid() {
console.log("\n\n====>In 'makeCanvasGrid()'!\n");
for(var canvasCounter = 0; canvasCounter < TOTAL_IMAGES; canvasCounter++) {
console.log("\n >FOR LOOP - canvasCounter = " + canvasCounter);
// 1. Create a new Canvas Object:
let newCanvas = document.createElement("canvas");
newCanvas.setAttribute("width", canvasWidth);
newCanvas.setAttribute("height", canvasHeight);
newCanvas.setAttribute("id", "newCanvas" + canvasCounter);
// Log-out just to verify the "id" property was set correctly:
console.log(" >newCanvas.id = " + newCanvas.id);
// 2. Place the Canvas at (x,y) (top, left) coordinates:
newCanvas.style.position = "absolute";
newCanvas.style.left = canvasX + "px"; //"100px";
newCanvas.style.top = canvasY + "px"; //"100px";
document.body.appendChild(newCanvas);
designOneHouse(newCanvas);
// Check the current Canvas' (X, Y) coords, and if needed, reset X to 0 and SKIP to the next "ROW" of Canvasses:
if(canvasCounter > 0 && canvasCounter % 3 == 0) {
console.log(" >>NEXT ROW PLEASE!!!! canvasCount = ", canvasCounter);
canvasX = 0;
canvasY += canvasHeight + 20;
}
else {
canvasX += canvasWidth + 10;
}
}
}
makeCanvasGrid();
#house-template {
position: absolute;
left: -1000px;
}
<div id="house-template">
<svg id="HOUSE" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="240.26" height="311.24" viewBox="0 0 240.26 311.24">
<defs>
<style>
.roof-class, .window-class, .door-class {stroke: #000;stroke-miterlimit: 10;}
</style>
</defs>
<g id="House">
<rect class="house-class" x="30.08" y="131.74" width="173.07" height="179"/>
<path d="M270,242V420H98V242H270m1-1H97V421H271V241Z" transform="translate(-67.39 -109.76)"/>
</g>
<polygon id="Roof" class="roof-class" points="1.11 131.74 239.11 131.74 117.11 0.74 1.11 131.74"/>
<rect id="Window2" class="window-class" x="145.11" y="160.74" width="30" height="42"/>
<rect id="Window1" class="window-class" x="58.61" y="160.74" width="30" height="42"/>
<rect id="Door" class="door-class" x="92.11" y="228.74" width="52" height="82"/>
</svg>
</div>
Below is one way to produce your desired result.
The approach below has the <svg> element in the HTML to be used as a template. That template is cloned, colors applied, converted into an Image and placed into the canvas for each house that has colors.
Note: the structure of the SVG changed. The class attributes are replaced by a custom data- attribute data-part that is used to apply the fill style through a normal CSS selector.
The coordinate positions of each house are in an array of space separated x y coordinates. The array also indicates how many houses are to be drawn
The colors for the house 'parts' are included in an Object that lists the house 'part' and its corresponding color (the count of colors should match the number of houses)
All <canvas> CSS is moved to the stylesheet.
I'll let you deal with sizing the image on the canvas.
const canvas = document.querySelector('canvas');
const context = canvas.getContext("2d");
const housePositions = ["0 10", "85 10", "170 10"];
const parts = {
House: ["fuchsia", "gold", "lightblue"],
Window: ["yellow", "pink", "lightgreen"],
Roof: ["maroon", "crimson", "darkred"],
Door: ["darkBlue", "purple", "darkslategray"]
};
function addHouse(colorIndex, x, y) {
let clonedSvgElement = document.querySelector('svg').cloneNode(true);
Object.keys(parts)
.forEach(part => {
clonedSvgElement.querySelectorAll(`[data-part=${part}]`)
.forEach(item => {
item.style.fill = parts[part][colorIndex];
});
const blob = new Blob([clonedSvgElement.outerHTML], { type: 'image/svg+xml;charset=utf-8' });
const blobURL = URL.createObjectURL(blob);
const image = new Image();
image.onload = () => {
context.drawImage(image, x, y, 130, 110);
URL.revokeObjectURL(this.src);
};
image.src = blobURL;
});
}
housePositions.forEach((coordString, index) => {
const [x, y] = coordString.split(' ');
addHouse(index, x, y);
});
canvas {
position: absolute;
left: 10px;
top: 10px;
width: 150px;
height: 80px;
border: 1px solid;
background-color: lightblue;
}
svg {
display: none;
}
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="index.css">
<title>Document</title>
<script defer src="index.js"></script>
</head>
<body>
<canvas></canvas>
<svg id="HOUSE" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="140" height="140" viewBox="0 0 240.26 311.24"><defs></defs><g id="House"><rect data-part="House" x="30.08" y="131.74" width="173.07" height="179"/><path d="M270,242V420H98V242H270m1-1H97V421H271V241Z" transform="translate(-67.39 -109.76)"/></g><polygon data-part="Roof" points="1.11 131.74 239.11 131.74 117.11 0.74 1.11 131.74"/><rect data-part="Window" x="145.11" y="160.74" width="30" height="42"/><rect data-part="Window" x="58.61" y="160.74" width="30" height="42"/><rect data-part="Door" x="92.11" y="228.74" width="52" height="82"/></svg>
</body>
</html>

Interactjs with SVG

I am using Interactjs to drag SVG elements around an SVG viewport, mostly because of it's support for gestures which I will eventually use for rotation on mobile. But I am having a problem with simple dragging.
<div class="container">
<!--?xml version="1.0" encoding="UTF-8"?-->
<svg viewBox="0 0 100 100">
<g class="shape" transform="scale(0.1) translate(-300, 0) rotate(10)">
<polygon xmlns="http://www.w3.org/2000/svg" points="350,75 379,161 469,161 397,215 423,301 350,250 277,301 303,215 231,161 321,161" />
</g>
</svg>
</div>
Interactjs doesn't give the current cursor x,y,so it is computed from a delta x,y, (event.dx, event.dy) accumulated over events, offset from the starting position (event.x0, event.y0). I then translate this into local coordinates for the target element (in case it has been rotated, skewed, etc.) (as per this SO answer) before applying it as a translation transform upon the element's current transform.
interact('.shape').draggable({
onstart: dragStartListener,
onmove: dragMoveListener,
onend: dragEndListener
});
var svg = document.getElementsByTagName('svg')[0];
var pt = svg.createSVGPoint();
function dragStartListener(event) {
event.target.transform.baseVal.consolidate();
console.clear();
}
function dragMoveListener(event) {
var target = event.target;
var currentCursor = getCurrentCursor(event);
var translationMatrix = getMatrixForTranslation(target, currentCursor.x, currentCursor.y);
target.transform.baseVal.getItem(0).setMatrix(target.transform.baseVal.getItem(0).matrix.multiply(translationMatrix));
}
function dragEndListener(event) {
var target = event.target;
target.removeAttribute('data-x');
target.removeAttribute('data-y');
}
function getMatrixForTranslation(target, x, y) {
return target.ownerSVGElement.createSVGMatrix().translate(x, y);
}
function getCurrentCursor(event) {
var target = event.target;
// keep the change in cursor on page in the data-x/data-y attributes
var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
pt.x = event.x0 + x; // calculate the new whole page coordinates
pt.y = event.y0 + y;
// convert to object space coordinates ... https://stackoverflow.com/a/5223921/5610106
var globalPoint = pt.matrixTransform(svg.getScreenCTM().inverse());
var globalToLocal = target.getTransformToElement(svg).inverse();
var inObjectSpace = globalPoint.matrixTransform(globalToLocal);
console.log('cursor: x: ' + inObjectSpace.x + '; y: ' + inObjectSpace.y);
return {x: inObjectSpace.x, y: inObjectSpace.y};
}
JSFiddle here.
I am manipulating the element's "transform" attribute, via target.transform.baseVal.getItem(0).setMatrix(), rather than via CSS transforms since I need the units to be in the same units of the SVG space. Also, I can easily consolidate() them into a single matrix I can send back to the server so the shapes can be rendered easily on other views.
But simple dragging is causing a large jump on first event, and then the target element, while following the cursor, is not under the cursor.
Any ideas on how I should be doing this?
I did this some time ago:
<script>
var ns = 'http://www.w3.org/2000/svg'
var svg = document.createElementNS( ns,'svg')
svg.id = 'svg'
svg.style.height = '100%'
svg.style.width = '100%'
rect = document.createElementNS( ns,'rect')
rect.setAttribute('width', '100px')
rect.setAttribute('height', '100px')
rect.setAttribute('fill', 'green')
svg.append(rect )
document.body.append(svg)
svg = document.getElementById('svg')
var mousedown = false;
window.onmousemove = (e) => {
var x = e.clientX
var y = e.clientY
svg.onmousedown = (e) => {
var x = e.clientX
var y = e.clientY
diffX = Math.abs(e.target.getBoundingClientRect().left - x)
diffY = Math.abs(e.target.getBoundingClientRect().top - y )
console.log(diffY +' || '+e.target.getBoundingClientRect().top+' || '+y)
mousedown = true;
}
if(mousedown) {
e.target.setAttribute('x', x - diffX)
e.target.setAttribute('y', y - diffY)
window.onmouseup = () => {
mousedown = false;
}
}
}
</script>

How to add hyperlinks in text in SVG with Javascript?

I thought that my code here would work when a user sends a message that includes a http://, but it doesn't:
function showMessage(nameStr, contentStr, textColor) {
var node = document.getElementById("chatbox");
var nameNode = document.createElementNS("http://www.w3.org/2000/svg", "tspan", textColor);
nameNode.setAttribute("x", 100);
nameNode.setAttribute("dy", 20);
nameNode.setAttribute("fill", textColor);
nameNode.appendChild(document.createTextNode(nameStr));
node.appendChild(nameNode);
var contentNode = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
contentStr = contentStr.replace(/((http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?)/g,
'<a target="blank" href="$1">$1</a>');
contentNode.setAttribute("x", 200);
contentNode.setAttribute("fill", textColor);
contentNode.innerHTML = contentStr;
// Add the name to the text node
node.appendChild(contentNode);
}
Can anyone find an error within this code?
nameStr is the name of the person sending the message,
contentStr is what the user input, and which the program should automatically change so any hyperlinks become clickable links, and
textColor is just the color of the message.
To make hyperlinks work inside an svg element, you should set up the XLink namespace, in addition to the default one for svg:
<svg width="500" height="500"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
Then you can use the xlink:href attribute:
<a xlink:href="http://www.example.com">click here</a>
Taking it all together in this snippet:
function showMessage(nameStr, contentStr, textColor) {
var node = document.getElementById("chatbox");
var nameNode = document.createElementNS("http://www.w3.org/2000/svg", "tspan", textColor);
nameNode.setAttribute("x", 100);
nameNode.setAttribute("dy", 20);
nameNode.setAttribute("fill", textColor);
nameNode.appendChild(document.createTextNode(nameStr));
node.appendChild(nameNode);
var contentNode = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
contentStr = contentStr.replace(/((http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?)/g,
'<a fill="purple" target="blank" xlink:href="$1">$1</a>'); // "xlink:" added!
contentNode.setAttribute("x", 200);
contentNode.setAttribute("fill", textColor);
contentNode.innerHTML = contentStr;
// Add the name to the text node
node.appendChild(contentNode);
}
// Sample call:
showMessage('John Doe', 'click this http://www.example.com', 'blue');
a {
text-decoration: underline;
}
<svg width="500" height="500"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1">
<text id="chatbox"></text>
</svg>

Measure not yet created SVG text in javascript

I'm trying to create a function that will measure how big a text element will be in a SVG element. The code examples I found at Stack Overflow does not work and gives a width of zero. If I delay the measurement I can get the text, but not right away. How is this solved?
var messureSVGtext = function(text, svg, options){
var text = document.createElementNS(svgns, 'text');
text.style.fontFamily = options.font;
text.setAttribute("style",
"font-family:" + options.font + ";" +
"font-size:" + options.fontSize + "px;"
);
var textNode = document.createTextNode(text);
text.appendChild(textNode);
svg.appendChild(text);
// This does not work
console.log(text.clientWidth);
//This does
setTimeout(function(){
console.log(text.clientWidth);
}, 100);
}
You can get the "computed style" of an element and then check the width & height from that.
Give the element an id attribute and after it is appended to the DOM, try this:
var elem1 = document.getElementById("text_elem");
var style = window.getComputedStyle(elem1, null);
console.log(style.width, style.height);
Working example
SVG
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="640"
height="480"
viewBox="0 0 640 480"
style="display:block">
<text id="svg_text" x="20" y="50" style="font-family:Arial; font-size:36px; fill:#BADA55">Hello World!</text>
</svg>
JavaScript
function getCompStyle(oid, cbf)
{
var obj, stl, itv;
obj = document.getElementById(oid);
itv = setInterval(function()
{
stl = window.getComputedStyle(obj, null);
if (stl && (stl.width.indexOf('0') != 0))
{
clearInterval(itv);
cbf(stl);
}
},0);
}
getCompStyle('svg_text', function(style)
{
console.log(style.width);
});
To use the example, place the SVG in your HTML <body> and the JavaScript in a <script> tag below the SVG - also in the <body>.

HTML5/Javascript - Perfect example of Drag and Drop between multiple canvases not working

I have have a problem I am hoping someone will be able to help with...
On my application I require the facility to be able to drag and drop images between multiple canvases.
There are a few pre-made examples of dragging and dropping between multiple canvases avaliable online, and I have found the perfect example for my needs courtesy of 'Richard Heyes' of RGraph which you can see here (NOTE: you must click the image before you can start dragging it).
As you can see, on his website this drag and drop feature works perfectly, however when I transfer the javascript, html and css to my application the ability to drag and drop the image does not work.
What I am doing:
<!DOCTYPE html>
<html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
<style type="text/css">
canvas {
border: 1px solid #808080;
}
</style>
<canvas style="float: left" height="125" width="400" id="cvs1">[No canvas support]</canvas>
<canvas style="float: left; margin-left: 100px" height="125" width="400" id="cvs2">[No canvas support]</canvas>
<script>
window.onload = function ()
{
var canvas1 = document.getElementById("cvs1");
var canvas2 = document.getElementById("cvs2");
var context1 = canvas1.getContext('2d');
var context2 = canvas2.getContext('2d');
var imageXY = {x: 5, y: 5};
/**
* This draws the image to the canvas
*/
function Draw ()
{
//Clear both canvas first
canvas1.width = canvas1.width
canvas2.width = canvas2.width
//Draw a red rectangle around the image
if (state && state.dragging) {
state.canvas.getContext('2d').strokeStyle = 'red';
state.canvas.getContext('2d').strokeRect(imageXY.x - 2.5,
imageXY.y - 2.5,
state.image.width + 5,
state.image.height + 5);
}
// Now draw the image
state.canvas.getContext('2d').drawImage(state.image, imageXY.x, imageXY.y);
}
canvas2.onclick =
canvas1.onclick = function (e)
{
if (state && state.dragging) {
state.dragging = false;
Draw();
return;
}
var mouseXY = RGraph.getMouseXY(e);
state.canvas = e.target;
if ( mouseXY[0] > imageXY.x
&& mouseXY[0] < (imageXY.x + state.image.width)
&& mouseXY[1] > imageXY.y
&& mouseXY[1] < (imageXY.y + state.image.height)) {
state.dragging = true;
state.originalMouseX = mouseXY[0];
state.originalMouseY = mouseXY[1];
state.offsetX = mouseXY[0] - imageXY.x;
state.offsetY = mouseXY[1] - imageXY.y;
}
}
canvas1.onmousemove =
canvas2.onmousemove = function (e)
{
if (state.dragging) {
state.canvas = e.target;
var mouseXY = RGraph.getMouseXY(e);
// Work how far the mouse has moved since the mousedon event was triggered
var diffX = mouseXY[0] - state.originalMouseX;
var diffY = mouseXY[1] - state.originalMouseY;
imageXY.x = state.originalMouseX + diffX - state.offsetX;
imageXY.y = state.originalMouseY + diffY - state.offsetY;
Draw();
e.stopPropagation();
}
}
/**
* Load the image on canvas1 initially and set the state up with some defaults
*/
state = {}
state.dragging = false;
state.canvas = document.getElementById("cvs1");
state.image = new Image();
state.image.src = 'http://www.rgraph.net/images/logo.png';
state.offsetX = 0;
state.offsetY = 0;
state.image.onload = function ()
{
Draw();
}
}
</script>
</body>
</html>
<!-- CODE COURTESY OF RICHARD HEYES OF RGRAPH
http://www.rgraph.net/blog/2013/january/an-example-of-html5-canvas-drag-n-drop.html -->
I have created the same thing on this JSFiddle but the dragging and dropping still does not work.
I am new to html5 and javascript so I am sure it must be something very simple I am overlooking, but I cannot work out what it is.
Your help with this would be much appreciated, thank you very much.
I have inserted your JavaScript code between tags <script> and </script> and move it from JavaScript to HTML and I have added new script link from the example page:
<script src="http://www.rgraph.net/libraries/RGraph.common.core.js" ></script>
JSFiddle - working example
So I think, that you must download and insert core files of RGraph to your code from this page.

Categories