Drag an image and drop on top of a SVG element - html

I've created two circles using SVG and an image. I'm trying to drag the image into the circles and while I'm able to do so after dropping the image it is not being visible. How can I drop it on top of the circles.
<!DOCTYPE html>
<html>
<body>
<div id="circle" >
<svg id="dest" ondrop="drop(event)" ondragover="allowDrop(event)" width="250" height="100">
<circle id="yelcirc" cx="50" cy="50" r="50" fill="yellow" />
<circle id="greencirc" cx="160" cy="50" r="50" fill="green" />
</svg>
</div>
<img id="draglogo" src="logo.gif" draggable="true" ondragstart="drag(event)" class="draggable" ondragend="" width="105" height="73">
</body>
<script>
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
</script>
</html>

Apparently the ondrop and ondragover events are not detected on your svg tag. On top of that, images in SVG don't have the same syntax than in regular HTML.
This is a simple example of how you can achieve the basics of what you want to do, of course there are some adjustments to do, the position of the image, its size etc. So basically what i do here is getting the original image attributes to create an SVG image. You could also have a regular image placed outside of the SVG tag, but i'm not sure it will be easier for positioning and such.
You can also read this answer about emulating the drag events on SVG elements
NOTE: this works only for the first drag, even if the image still looks draggable after being moved, the function will throw an error because of the way img is selected from the DOM, it has been removed, so the img tag is not found anymore.
<!DOCTYPE html>
<html>
<body>
<div id="circle" ondrop="drop(event)" ondragover="allowDrop(event)" >
<svg id="dest" width="250" height="100">
<circle id="yelcirc" cx="50" cy="50" r="50" fill="yellow" />
<circle id="greencirc" cx="160" cy="50" r="50" fill="green" />
</svg>
</div>
<img id="draglogo" src="https://placeimg.com/105/73/any" draggable="true" ondragstart="drag(event)" class="draggable" ondragend="" width="105" height="73">
</body>
<script>
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text"),
img = document.getElementById(data),
imgSrc = img.getAttribute('src'),
imgW = img.getAttribute('width'),
imgH = img.getAttribute('height'),
//for example you can calculate X position from event circle
imgX = ev.target.getAttribute('cx') - ev.target.getAttribute('r');
ev.target.parentElement.innerHTML += '<image xlink:href="' + imgSrc + '" x="' + imgX + '" y="0" width="' + imgW + 'px" height="' + imgH + 'px"/>';
img.parentNode.removeChild(img);
}
</script>
</html>

Related

SVG mask used in SVG embedded in HTML document not working

I am making an HTML document version of a griefer list for Dredark.io because the ship I made that had the list was ruined. An idiot I thought was my friend went promoting everyone who joined to captain rank and they all messed EVERYTHING up. However, I am having issues with the portion that shows the last seen appearance of each griefer: the SVG mask="url(#face/body/legs/hand)" attribute is not working.
<!DOCTYPE html>
<html>
<head>
<title>Dredark.io Griefers</title>
<script>
var list = {
"clown": {
warning: 15,
hair: "none",
skin: "#ff8800",
shirt: "#111111",
pants: "#000000",
proof: "Many people know him as a griefer"
},
"SMARTLABRAT": {
warning: 1,
hair: "none",
skin: "#ffeeaa",
shirt: "#11aa11",
pants: "#1122aa",
proof: "none"
}
};
function load(){
var max = 0;
var sorted = [];
for(var prop in list){
if(list[prop].warning >= max){
max = list[prop].warning;
}
}
var cur = max;
for(var i = max; i > 0; i--){
for(var prop in list){
if(list[prop].warning == i){
sorted.push(prop);
}
}
}
var html = "";
var warningColors = ["#00ff00","#44ff00","#88ff00","#aaff00","#ffff00","#ffaa00","#ff8800","#ff4400","#ff0000"];
for(var i = 0; i < sorted.length; i++){
var color = "";
for(var j = 0; j < warningColors.length; j++){
if(list[sorted[i]].warning >= (j * 10)){
color = warningColors[j];
}
}
html = "<tr><td>" + (i + 1) + "</td><td>" + sorted[i] + "</td><td style='background: " + color + ";'>" + list[sorted[i]].warning + "</td><td>" + list[sorted[i]].proof + "</td><td>" + document.getElementById("skin").outerHTML.replace(/!----/g,"#00ff00").replace("!_---","#222222").replace("!-_--","#111111") + "</td></tr>";
document.getElementById("list").innerHTML += html;
}
}
window.onload = function(){
load();
};
</script>
<style>
#list{
border: 1px solid #004400;
}
#prototypes{
display: none;
}
</style>
</head>
<body>
<details>
<summary>Because the previous one was ruined</summary>
<p>by <b>La Danse Macabre</b>, someone I trusted.</p>
<details>
<summary>Note to <b>La Danse Macabre</b>:</summary>
<p>Do not promote random people to <u>Captain</u> on <b>MY</b> ships!</p>
</details>
</details>
<p>The warning level of a griefer depends on how many ships they griefed and how much hard work is lost to them per ship. Griefers with a level below <code>20</code> are not really that bad and can still be trusted. Griefers with a level of less than <code>5</code> mean that they are possible but not proven.</p>
<div id="prototypes">
<svg id='skin' xmlns='http://www.w3.org/2000/svg' width='50px' height='80px' viewbox='0 0 50 80'>
<defs>
<mask maskContentUnits="userSpaceOnUse" id="face" width="50" height="80" viewbox="0 0 50 80"><image href="https://test.drednot.io/img/player_head.png" x="0" y="0"/></mask>
<mask maskContentUnits="userSpaceOnUse" id="body"><image href="https://test.drednot.io/img/player.png" x="17" y="7"/>
<image href="https://test.drednot.io/img/player_arm.png" x="20" y="-5"/></mask>
<mask maskContentUnits="userSpaceOnUse" id="legs"><image href="https://test.drednot.io/img/player_leg.png" x="17" y="8"/>
<image href="https://test.drednot.io/img/player_leg.png" x="20" y="8"/></mask>
<mask maskContentUnits="userSpaceOnUse" id="hand"><image href="https://test.drednot.io/img/player_hand.png" x="19" y="-5"/></mask>
</defs>
<rect x="0" y="0" width="100" height="100" fill="!----" mask="url(#face)"/>
<rect x="0" y="0" width="100" height="100" fill="!_---" mask="url(#body)"/>
<rect x="0" y="0" width="100" height="100" fill="!-_--" mask="url(#legs)"/>
<rect x="0" y="0" width="100" height="100" fill="!----" mask="url(#hand)"/>
<image href="https://test.drednot.io/img/player_face.png" x="0" y="0"/>
<image href="https://test.drednot.io/img/player_foot.png" x="17" y="8"/>
<image href="https://test.drednot.io/img/player_foot.png" x="20" y="8"/>
</svg>
</div>
<table id="list" border="1" cellSpacing="0px">
<tr>
<th>#</th>
<th>Name</th>
<th>Warning level</th>
<th>Proof</th>
<th>Last seen appearance</th>
</tr>
</table>
</body>
</html>
As its own file, the SVG image DOES support it, but for some reason when I embed it within the HTML document it just stops working. The best I can do is make the whole thing disappear. I have tried everything and I have been searching the whole web for a fix but I have lost all hope other than you kind people. What am I doing wrong and how do I fix this?
Svg <mask> won't be applied, if your svg containing it is hidden via display: none.
But you can hide it like so
#prototypes {
position: absolute;
width.0;
height: 0;
overflow: hidden;
}
This also applies to other svg elements like <clipPath> or filters.
So It's actually a better practice to hide your svg containing all assets and definitions this way.
Workin example
var list = {
"clown": {
warning: 15,
hair: "none",
skin: "#ff8800",
shirt: "#111111",
pants: "#000000",
proof: "Many people know him as a griefer"
},
"SMARTLABRAT": {
warning: 1,
hair: "none",
skin: "#ffeeaa",
shirt: "#11aa11",
pants: "#1122aa",
proof: "none"
}
};
function load() {
var max = 0;
var sorted = [];
for (var prop in list) {
if (list[prop].warning >= max) {
max = list[prop].warning;
}
}
var cur = max;
for (var i = max; i > 0; i--) {
for (var prop in list) {
if (list[prop].warning == i) {
sorted.push(prop);
}
}
}
var html = "";
var warningColors = ["#00ff00", "#44ff00", "#88ff00", "#aaff00", "#ffff00", "#ffaa00", "#ff8800", "#ff4400", "#ff0000"];
for (var i = 0; i < sorted.length; i++) {
var color = "";
for (var j = 0; j < warningColors.length; j++) {
if (list[sorted[i]].warning >= (j * 10)) {
color = warningColors[j];
}
}
html = "<tr><td>" + (i + 1) + "</td><td>" + sorted[i] + "</td><td style='background: " + color + ";'>" + list[sorted[i]].warning + "</td><td>" + list[sorted[i]].proof + "</td><td>" + document.getElementById("skin").outerHTML.replace(/!----/g, "#00ff00").replace("!_---", "#222222").replace("!-_--", "#111111") + "</td></tr>";
document.getElementById("list").innerHTML += html;
}
}
window.onload = function() {
load();
};
#list {
border: 1px solid #004400;
}
#prototypes {
position: absolute;
width.0;
height: 0;
overflow: hidden;
}
<details>
<summary>Because the previous one was ruined</summary>
<p>by <b>La Danse Macabre</b>, someone I trusted.</p>
<details>
<summary>Note to <b>La Danse Macabre</b>:</summary>
<p>Do not promote random people to <u>Captain</u> on <b>MY</b> ships!</p>
</details>
</details>
<p>The warning level of a griefer depends on how many ships they griefed and how much hard work is lost to them per ship. Griefers with a level below <code>20</code> are not really that bad and can still be trusted. Griefers with a level of less than <code>5</code> mean that they are possible but not proven.</p>
<div id="prototypes">
<svg aria-hidden="true" id='skin' xmlns='http://www.w3.org/2000/svg' width='50px' height='80px' viewbox='0 0 50 80'>
<defs>
<mask maskContentUnits="userSpaceOnUse" id="face" width="50" height="80" viewbox="0 0 50 80"><image href="https://test.drednot.io/img/player_head.png" x="0" y="0"/></mask>
<mask maskContentUnits="userSpaceOnUse" id="body"><image href="https://test.drednot.io/img/player.png" x="17" y="7"/>
<image href="https://test.drednot.io/img/player_arm.png" x="20" y="-5"/></mask>
<mask maskContentUnits="userSpaceOnUse" id="legs"><image href="https://test.drednot.io/img/player_leg.png" x="17" y="8"/>
<image href="https://test.drednot.io/img/player_leg.png" x="20" y="8"/></mask>
<mask maskContentUnits="userSpaceOnUse" id="hand"><image href="https://test.drednot.io/img/player_hand.png" x="19" y="-5"/></mask>
</defs>
<rect x="0" y="0" width="100" height="100" fill="!----" mask="url(#face)"/>
<rect x="0" y="0" width="100" height="100" fill="!_---" mask="url(#body)"/>
<rect x="0" y="0" width="100" height="100" fill="!-_--" mask="url(#legs)"/>
<rect x="0" y="0" width="100" height="100" fill="!----" mask="url(#hand)"/>
<image href="https://test.drednot.io/img/player_face.png" x="0" y="0"/>
<image href="https://test.drednot.io/img/player_foot.png" x="17" y="8"/>
<image href="https://test.drednot.io/img/player_foot.png" x="20" y="8"/>
</svg>
</div>
<table id="list" border="1" cellSpacing="0px">
<tr>
<th>#</th>
<th>Name</th>
<th>Warning level</th>
<th>Proof</th>
<th>Last seen appearance</th>
</tr>
</table>

CSS styles not applied to SVG when SVG is rendered on Canvas

I am styling an SVG image using CSS in a separate file. Then I am rendering the SVG onto a canvas that can be saved as a PNG.
The SVG receives the CSS styles properly when it is just an SVG element on an HTML page, and renders as expected. However, when the SVG is rendered in a canvas element, the styles are not applied.
Is it possible to use external CSS to style an SVG and save that to a canvas without losing the styles? I cannot use inline CSS due to Content Security Policy in the browser.
Here is a sample.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>svg to png</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<button>svg to png</button>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="200" height="200">
<path d="M10 40, C20 20, 50 20, 70 30, 70 30, 110 50, 160 20" id="path-1" fill="#CCCCCC" />
<text class="text-red">
<textpath xlink:href="#path-1" startOffset="50%" text-anchor="middle">Sample Path Text</textpath>
</text>
<rect x="10" y="14" width="10" height="10" />
<text x="0" y="100" class="text-primary">Text Style 1</text>
<text x="0" y="150" class="text-secondary">Text Style 2</text>
</svg>
<canvas id="canvas"></canvas>
<script src="./script.js"></script>
</body>
</html>
style.css
.text-primary {
font-size: 24px;
font-family: calibri;
}
.text-secondary {
font-size: 12px;
font-family: arial;
}
.text-red {
fill: #ff0000;
}
script.js
var btn = document.querySelector("button");
var svg = document.querySelector("svg");
var canvas = document.querySelector("canvas");
btn.addEventListener("click", function () {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var data = new XMLSerializer().serializeToString(svg);
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svgBlob = new Blob([data], { type: "image/svg+xml;charset=utf-8" });
var url = DOMURL.createObjectURL(svgBlob);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
var imgURI = canvas
.toDataURL("image/png")
.replace("image/png", "image/octet-stream");
};
img.src = url;
});
Here is a render of the basic example, svg on the left and canvas on the right.
css styles applied to svg on left, css styles not applied to svg on canvas on right

How to prevent preservation of aspect ratio in svg.js

I am trying to render a rectangle with svg.js (https://svgjs.dev) and NOT preserve the aspect ratio when the parent/window is resized. It works fine for a plain svg element but not for the svg.js elements:
https://jsfiddle.net/h8sgown7/3/
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.4/svg.min.js"></script>
<script type="text/javascript">
SVG.on(document, 'DOMContentLoaded', function() {
var draw = SVG('drawing')
var rect = draw.rect(300, 50).move(0, 0).fill('#ff0000');
draw.viewbox(0, 0, 300, 55);
draw.attr('preserveAspectRatio', 'none');
})
</script>
<title>SVGTest</title>
<style>
.drawing
{
width: 100%;
height:50px;
}
</style>
</head>
<body>
<div id="drawing"></div>
<svg width="100%" height="100px" viewbox="0 0 100 100" preserveAspectRatio="none">
<rect width="100" height="100" style="fill:rgb(0,0,255)" />
</svg>
</body>
</html>
You have set the height to 100px in your plain svg element. By default the svg is set to width="100%" height="100%" by svg.js.
Just change your svg.js generated svg height to match your inline svg.
draw.viewbox(0, 0, 300, 55).height('100px');
https://jsfiddle.net/h8sgown7/7/

SVG image within an object tag overlys website content

I am using a SVG image within an object tag on a web page. I cannot use the img-tag, because I need to interact with CSS classes within the SVG and have a navigation option which is used on clicks within the SVG.
I used to have the SVG inline, but now I wanted to rework the page to be adaptive/responsive and thus I’m trying to use the object-tag to make it scalable. It works fine with Chrome, Firefox and Opera. When it comes down to IE I ran into problems.
Other than the other browsers IE overlays everything with a white background. This includes the navigation div I use to show when the user clicks on certain elements. I simplified everything to show the effect:
This is how it should be
What IE makes of it
I tried to fiddle with Z-index on the object, wrapping the object in a different div and applying z-index on it. I also tried CSS on the SVG and the object tag, trying to set the background to transparent, none and similar things. All to no avail. The background stays white and the content hidden.
Then I found this:
http://solidlystated.com/scripting/place-div-over-iframe-in-internet-explorer/
Which leads to IE showing the content, but makes the element inactive, since the iframe prevents the links within to be clicked:
The simplified HTML so far:
<body>
<div class="wrapper">
<h1>System</h1>
<div class="contentItem">
<div class="aroundSVG" style="height: 530px">
<object type="image/svg+xml" data="/img/sys2.svg" id="svgObject" style="width: 100%; height: 100%; background: transparent;"></object>
</div>
<div id="showNav">
<ul>
<li><a href=„LinkTarget“ onclick="location.href = this.href + fncTarget + '/index.htm';
return false;">Line 1</a></li>
<li><a href=„LinkTarget“ onclick="location.href = this.href + fncTarget + '/event.htm';
return false;">Line 2</a></li>
<li><a href=„LinkTarget“ onclick="location.href = this.href + fncTarget + '/error.htm';
return false;">Line 3</a></li>
<li><a href=„LinkTarget“ onclick="location.href = this.href + fncTarget + '/index.htm?reset=true';
return false;">Line 4</a></li>
</ul>
<!-- <iframe id="fake" src="about:blank" style="width: 100%; height: 100%; position: absolute; top: 1px; left: 1px; border: none;"></iframe> -->
</div>
</div>
</body>
The SVG file:
<head>
<meta http-equiv="content-type" content="image/svg+xml"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</meta>
<link rel="stylesheet" type="text/css" href="/css/svgStyle.css" media="all" />
<script type="text/javascript" src="/js/jquery_min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
parent.svgObj = document
parent.svgModules = $(parent.svgObj).find(".modules");
parent.setModClick();
$(parent.svgObj).click(function (e)
{
var container = parent.navContainer;
var optDiv = $(".classB");
if (!container.is(e.target)
&& container.has(e.target).length === 0
&& !optDiv.is(e.target)
&& optDiv.has(e.target).length === 0)
{
container.hide(250);
}
});
})
</script>
</head>
<svg viewBox="0 0 923 500">
<g class=„classA classB“ id="gvg" transform="translate(300,250)">
<circle class=„circle“ cx="150" cy="0" r="40" stroke="black" stroke-width="3" fill="red" />
</g>
<g class=„classA classB“ id="dr" transform="translate(300,300)">
<circle class=„circle“ cx="150" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</g>
<g class=„classA classB“ id="hur" transform="translate(300,350)">
<circle class=„circle“ cx="150" cy="100" r="40" stroke="black" stroke-width="3" fill="red" />
</g>
</svg>
Is there any way to keep the toggled menu active and shown within IE/Edge (I guess there will be problems, too), or am I using the object-tag/SVG file the wrong way?
There seems no way to resolve this with CSS within IE, while the other browser keep the SVG background transparant.
After some experimenting and researching I found a solution. I needed to make the <ul> element absolute in positioning and remove it from the fake <iframe>. Now the navigation is visible and clickable. I needed to add some CSS and fiddle with height and width in javascript/jQuery due to variable heights, but that's irrelevant.
So it worked for me like this:
<div id="showNav">
<ul style='position: absolute'>
<li><a href=„LinkTarget“ onclick="location.href = this.href + fncTarget + '/index.htm';
return false;">Line 1</a></li>
<li><a href=„LinkTarget“ onclick="location.href = this.href + fncTarget + '/event.htm';
return false;">Line 2</a></li>
<li><a href=„LinkTarget“ onclick="location.href = this.href + fncTarget + '/error.htm';
return false;">Line 3</a></li>
<li><a href=„LinkTarget“ onclick="location.href = this.href + fncTarget + '/index.htm?reset=true';
return false;">Line 4</a></li>
</ul>
<iframe id="fake" src="about:blank" style="width: 102%; height: 103%; border: none;"></iframe>
</div>
You may need to vary height, width and border of the iFrame to match your case, since you need to adjust this to make all formatting of the parent <div> visible.
I hope this will help someone who stumbles over the same issues.

How to create a clickable grid of triangles using html, svg?

I have already created a grid of triangles like so:
svg {
margin-left: 0px;
margin-right: -60px;
padding: 0;
}
<div data-bind="foreach: Grid">
<div data-bind="foreach: $data.rowData">
<!-- ko if: $data.owner() === 0 && ($data.pos[0] + $data.pos[1])%2 === 0-->
<svg height="103.92" width="120">
<polygon class="" points="60,0 0,103.92 120,103.92" style="fill:grey;" data-bind="click: $root.test.bind($data, $data)" />
</svg>
<!-- /ko -->
<!-- ko if: $data.owner() === 0 && ($data.pos[0] + $data.pos[1])%2 === 1-->
<svg height="103.92" width="120">
<polygon class="" points="0,0 120,0 60,103.92" style="fill:grey;" data-bind="click: $root.test.bind($data, $data)" />
</svg>
<!-- /ko -->
</div>
</div>
My problem is that only the left half of the triangles is clickable. I think this is due to the (still rectangular) shape of the svg-element. But I have no idea how to fix this. Is there any way to make every triangle clickable in its whole area?
At the moment, all your individual SVGs are overlapping one another and any click that misses a triangle will be swallowed by the parent <svg> element.
The cleanest solution would be to put all your polygons in one big SVG. However there is another way around your problem using the pointer-events property.
Set pointer-events="none" on your <svg> elements so that clicks will pass through them. But you'll also need to set an explicit pointer-events="fill" on your polygons, since otherwise they'll inherit the "none" from their parent SVGs.
var output = document.getElementById("output");
document.getElementById("red").addEventListener("click", function(e) {
output.textContent = "red";
});
document.getElementById("green").addEventListener("click", function(e) {
output.textContent = "green";
});
svg {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
}
polygon {
pointer-events: fill;
}
#output {
margin-top: 120px;
}
<svg width="100px" height="100px">
<polygon points="0,0,100,0,100,100" fill="red" id="red"/>
</svg>
<svg width="100px" height="100px">
<polygon points="0,0,100,100,0,100" fill="green" id="green"/>
</svg>
<div id="output"></div>
You should use one svg tag with both polygons inside it. This way the Square svg elements won't overlap each other:
polygon {
fill: grey;
}
polygon:hover {
fill: #000;
}
<svg height="103.92" width="185">
<polygon points="60,0 0,103.92 120,103.92" />
<polygon points="65,0 185,0 125,103.92" />
</svg>