I would like to create an animation with SVG and have here a few problems.
The small circle shall move along the path of the great circle.
Can someone here show me the way with a few hints or a simple example?
I tried it with css but this is not the right way to go.
<!doctype html>
<html lang="en">
<head>
</head>
<body>
<svg>
<circle id="c1" cx="100" cy="100" r="90" stroke="#ccc" stroke-width="2" fill="none"/>
<circle id ="c2" cx="100" cy="10" r="8" stroke="#f00" stroke-width="3" fill="#fff"/>
</svg>
<script>
circle1=document.getElementById("c1");
circle2=document.getElementById("c2");
for (i=1;i<60;i++){
// here is must calc the points
//of the path of the circle
//that is difficult but not such a problem
//but i donĀ“t see an animation
//but I see no animation
circle2.setAttribute("cx",100+i);
circle2.setAttribute("cy",100+i);
}
</script>
</body>
</html>
a good day and welcome,
I think the way to calculate the points of the circle is too elaborate. It is easier to rotate your circle with transform rotate.
Last week I answered a similar question in a seminar as follows:
(Please read it intensive and also the remarks. I hope you understand the code.)
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<svg id = "mySVG" width="200" height="200" viewBox="0 0 200 200">
<circle id = "myCirclePath" cx="100" cy="100" r="90" stroke="#ccc" stroke-width="2" fill="none"/>
<circle id = "myAniCircle" cx="100" cy="10" r="8" stroke="#f00" stroke-width="3" fill="#fff"/>
</svg>
<script>
var animateAtCircle = function (elementToAnimate, circlePath, duration, callback){
//I would see:
var imagesPerSecond = 60;
//and calculate the sum of steps i need:
var sumSteps = duration / (1000/imagesPerSecond);
//an circle has 360 degrees
//so my stepwidth is:
var stepWidth = 360 / sumSteps
//let us begin with step 1
var step = 1;
//before, i need a Variable to store my Timeout
var pathAnim;
//and begin with our animation function
var anim=function(){
//rotate the circle relative
//to the midpoint of circlePath
elementToAnimate.setAttribute("transform",`rotate(
${step*stepWidth},
${circlePath.getAttribute('cx')},
${circlePath.getAttribute('cy')}
)`);
//until step smaller then sumSteps
if ( step < sumSteps){
//set step to next step
step ++
//and wait for creating next rotation an call anim again...
pathAnim = setTimeout(anim, 1000/imagesPerSecond);
} else {
//animation is finished;
clearTimeout(pathAnim);
//call callback function
if (callback) return callback();
}
}
//now call our anim loop function
anim();
}
//now call our Aimation at circle...
animateAtCircle(myAniCircle,myCirclePath,5000, function(){console.log('finished');});
</script>
</body>
</html>
Related
I've got 3 files: test.html, test.js and test.svg
I'm trying to call the different files into HTML but the file svg don't work
test.html
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Using SVG as an object</title>
<link href="index.css" rel="stylesheet" />
</head>
<body>
<h1>Test</h1>
<script type="text/javascript" src="test.js"></script>
<object data="test.svg" width="300" height="300"> </object> <!-- Not working -->
<input type="button" value="Start Animation" onclick="startAnimation();">
<input type="button" value="Stop Animation" onclick="stopAnimation();">
</body>
</html>
test.js
var timerFunction = null;
function startAnimation() {
if(timerFunction == null) {
timerFunction = setInterval(animate, 20);
}
}
function stopAnimation() {
if(timerFunction != null){
clearInterval(timerFunction);
timerFunction = null;
}
}
function animate() {
var circle = document.getElementById("circle1");
var x = circle.getAttribute("cx");
var newX = 2 + parseInt(x);
if(newX > 500) {
newX = 20;
}
circle.setAttribute("cx", newX);
}
test.svg
<svg width="500" height="100">
<circle id="circle1" cx="20" cy="20" r="10"
style="stroke: none; fill: #ff0000;"/>
</svg>
I don't understand why I can't insert svg file with object
Thanks for your help
See Dev.To Post: <load-file> Web Component
Use a modern, native W3C standard Web Component <load-svg>
it reads the SVG as text
adds SVG to shadowDOM as DOM element
moves the style element from lightDOM to shadowDOM
So style is only applied to one SVG
<load-svg shadowRoot src="//graphviz.org/Gallery/directed/fsm.svg">
<style>
svg { height:150px } text { stroke: green } path { stroke: red ; stroke-width:3 }
</style>
</load-svg>
<load-svg src="//graphviz.org/Gallery/directed/fsm.svg">
<!-- all HTML here is overwritten -->
</load-svg>
<script>
customElements.define('load-svg', class extends HTMLElement {
async connectedCallback() {
this.style.display = 'none'; // prevent FOUC (provided Custom Element is defined ASAP!)
let src = this.getAttribute("src");
let svg = await (await fetch(src)).text();
if (this.hasAttribute("shadowRoot")) {
this.attachShadow({mode:"open"}).innerHTML = svg;
this.shadowRoot.append(this.querySelector("style") || []);
} else {
this.innerHTML = svg;
}
this.style.display = 'inherit';
}
});
</script>
More complex example: How to make an svg interactive to gather comments/annotations on depicted elements
You can use svgs directly in the HTML. Easiest is to just use the SVG inside the HTML. You can also re-use an svg shape on a page but then the icons have a shadow-dom boundary.
If you use an object or svg tag, it will render just fine but you will lose all information about classes, IDs and so on in the SVG.
Further Information on SVG on css-tricks
More information about how to group and re-use shapes in SVG on css-tricks (and one more, also on css-tricks)
var timerFunction = null;
function startAnimation() {
if (timerFunction == null) {
timerFunction = setInterval(animate, 20);
}
}
function stopAnimation() {
if (timerFunction != null) {
clearInterval(timerFunction);
timerFunction = null;
}
}
function animate() {
var circle = document.getElementById("circle1");
var x = circle.getAttribute("cx");
var newX = 2 + parseInt(x);
if (newX > 500) {
newX = 20;
}
circle.setAttribute("cx", newX);
}
<svg width="500" height="100">
<circle id="circle1" cx="20" cy="20" r="10"
style="stroke: none; fill: #ff0000;"/>
</svg>
<input type="button" value="Start Animation" onclick="startAnimation();">
<input type="button" value="Stop Animation" onclick="stopAnimation();">
<object> tags can be used on many elements, including SVG files, and therefore not recognized as image elements, So:
It's not available on image search. So you can use an <img> tag as fallback (optional but recommended)
You should specify the type of the object
So you can change it like this:
<object type="image/svg+xml" data="test.svg" width="300" height="300">
<img src="test.svg" />
</object>
And another problem is the SVG file. Based on MDN documents :
The xmlns attribute is only required on the outermost SVG element of SVG documents. It is unnecessary for inner SVG elements or inside HTML documents.
so you need to add xmlns parameters to the SVG tag on SVG file like this :
<svg width="500" height="100" xmlns="http://www.w3.org/2000/svg">
<circle id="circle1" cx="20" cy="20" r="10"
style="stroke: none; fill: #ff0000;"/>
</svg>
I made a small simple example that works, checkout this sandBox link
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title></title>
<style>
svg{ display: inline-block; }
</style>
<script src="my.js"></script>
</head>
<body>
<embed src="sprites.svg" type="image/svg+xml" width="0" height="0" />
<svg id="MAIN" xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 320 208" shape-rendering="crispEdges">
<path d="M0 0h320v208h-320v-208z" fill="#000"/> <!-- Fill screen -->
<svg id="sprite1"><use xlink:href="sprites.svg#SP1" x="64" y="128"</use></svg>
<svg id="sprite2"><use xlink:href="sprites.svg#SP2" x="64" y="128"</use></svg>
<svg id="sprite3"><use xlink:href="sprites.svg#SP3" x="64" y="128"</use></svg>
</svg>
<script>
start( 1 ); // Call Func in my.js
</script>
</body>
</html>
I have hundreds of sprites in "sprites.svg". How do I, in javascript add the sprites I want to the DOM via javascript. I want them added so that they are just like sprite1 to sprite3 in the example html page above, thanks Keith
Worked it out, not as complex as I thought.
var svgURI = "http://www.w3.org/2000/svg";
var mainSVG = document.getElementById( "MAIN" ); // my main SVG
var svg = document.createElementNS( svgURI, 'svg' );
svg.id = "sprite4";
svg.innerHTML = "<use xlink:href='sprites.svg#SP4'></use>";
mainSVG.appendChild( svg );
Thanks to every one who tried to help
I am designing a web page which has a lot of mathematical figures .e.g rhombus ,square ,rectangle,triangle etc....
what I am trying to achieve is ,when I click on any of the figures ,it should show up a msg /info kind of thing.
I have only one canvas id ,and lot of figures in it ,how i make them clickable so that I can display the required info from each figure
Here's how to use native html canvas to let users get a message about the shape they clicked:
for each shape, put their corner points in a javascript object
put all those shape-objects in an array
listen for mousedown events
in mousedown check if the mouse is inside each shape using context.isPointInPath
if the mouse is inside an object, display your message about that shape
A Demo: http://jsfiddle.net/m1erickson/wPMk5/
Example code:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
// canvas variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var scrollX=$canvas.scrollLeft();
var scrollY=$canvas.scrollTop();
// set styles
ctx.fillStyle="skyblue";
ctx.strokeStyle="lightgray";
ctx.lineWidth=2;
// create a triangle and parallelogram object
var triangle={
points:[{x:25,y:100},{x:50,y:50},{x:75,y:100}],
message:"I am a triangle"
}
var parallelogram={
points:[{x:150,y:50},{x:250,y:50},{x:200,y:100},{x:100,y:100}],
message:"I am a parallelogram"
}
// save the triangle and parallelogram in a shapes[] array
var shapes=[];
shapes.push(triangle);
shapes.push(parallelogram);
// function to draw (but not fill/stroke) a shape
// (needed because isPointInPath only tests the last defined path)
function define(shape){
var points=shape.points;
ctx.beginPath();
ctx.moveTo(points[0].x,points[0].y);
for(var i=1;i<points.length;i++){
ctx.lineTo(points[i].x,points[i].y);
}
}
// function to display a shape on the canvas (define + fill + stroke)
function draw(shape){
define(shape);
ctx.fill();
ctx.stroke();
}
// display the triangle and parallelogram
draw(triangle);
draw(parallelogram);
// called when user clicks the mouse
function handleMouseDown(e){
e.preventDefault();
// get the mouse position
var mouseX=parseInt(e.clientX-offsetX);
var mouseY=parseInt(e.clientY-offsetY);
// iterate each shape in the shapes array
for(var i=0;i<shapes.length;i++){
var shape=shapes[i];
// define the current shape
define(shape);
// test if the mouse is in the current shape
if(ctx.isPointInPath(mouseX,mouseY)){
// if inside, display the shape's message
alert(shape.message);
}
}
}
// listen for mousedown events
$("#canvas").mousedown(function(e){handleMouseDown(e);});
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
If you can use SVG instead you can achive this goal very easily.
Using http://svg-edit.googlecode.com/svn/branches/2.5.1/editor/svg-editor.html you can draw relevant shapes and you can get the svg code. Add onclick() events to it .
By this online tool you can create shapes
Click SVG to get the code
Code
<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg">
<!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
<g>
<title>Layer 1</title>
<rect id="svg_1" height="74" width="150" y="95" x="58" stroke-width="5" stroke="#000000" fill="#FF0000"/>
<ellipse ry="35" rx="42" id="svg_2" cy="216" cx="158" stroke-width="5" stroke="#000000" fill="#FF0000"/>
<ellipse ry="36" rx="80" id="svg_3" cy="123" cx="342" stroke-width="5" stroke="#000000" fill="#0000ff"/>
<path id="svg_4" d="m265,257l63,-59l82,47l-22,71l-84,5l-39,-64z" stroke-width="5" stroke="#000000" fill="#0000ff"/>
</g>
</svg>
You can add onclick events to this.And also following shows adding jquery title mouse over also.
<rect id="svg_29" class="popup_tool" title="Message you want to popup when mouse over" height="17" width="20" y="224" x="452" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="3" stroke="#000000" fill="#ff0000"/>
Hope you got the answer
simply wrap the canvas id inside a div element after that you can hide or show the div using js/jquery with that you can display your message
To easily achieve your task using canvas element you need a framework like fabricjs
I have a 'reference' svg with a couple of different groups defined in it for different icons.
Later I define an svg area and use one of the groups. That's working great.
I would like to be able to swap put the group that's referenced in the xlink:href when something is clicked.
The jsFiddle
<!-- SVG SOURCE -->
<svg height="0" width="0" style="position:absolute;margin-left: -100%;">
<g id="unchecked-icon">
<path d="M22.215,44.176c-12.11,0-21.963-9.851-21.963-21.962c0-12.11,9.852-21.962,21.963-21.962 c12.11,0,21.961,9.852,21.961,21.962C44.176,34.325,34.325,44.176,22.215,44.176z M22.215,2.557 c-10.839,0-19.658,8.818-19.658,19.657s8.818,19.658,19.658,19.658c10.839,0,19.657-8.818,19.657-19.658S33.054,2.557,22.215,2.557 z" />
</g>
<g id="checked-icon">
<path d="M22.215,44.176c-12.11,0-21.963-9.851-21.963-21.962c0-12.11,9.852-21.962,21.963-21.962 c12.11,0,21.961,9.852,21.961,21.962C44.176,34.325,34.325,44.176,22.215,44.176z M22.215,2.557 c-10.839,0-19.658,8.818-19.658,19.657s8.818,19.658,19.658,19.658c10.839,0,19.657-8.818,19.657-19.658S33.054,2.557,22.215,2.557 z" />
<polygon points="20.337,32.279 12.274,24.947 14.642,22.344 19.745,26.985 30.005,12.311 32.888,14.327 " />
</g>
</svg>
<!-- SVG SOURCE ends-->
<p>Intial state</p>
<svg class="icon svg" viewBox="0 0 44.429 44.429">
<use xlink:href="#unchecked-icon"></use>
</svg>
<p>After Click</p>
<svg class="icon svg checked" viewBox="0 0 44.429 44.429">
<use xlink:href="#checked-icon"></use>
</svg>
Ok, I found out how you can do this. You need to be changing the Attributes of your <use> DOM Element. In basic JavaScript you would do this with setAttribute() and in jQuery you use attr().
Give the <svg> that will use the graphic and the <use> tag that says which graphic to use ids so that you can grab them with code. After that, it's all about setting up the listeners.
You ask for jQuery, however this is completely doable without so I am providing both ways
JsFiddle
Vector Graphics Toggler
JavaScript
js/index.js
// Toggle to demonstrate both ways of doing this
var BasicJavaScript = true;
// See what way we want to do this
if (BasicJavaScript) {
// When our document loads
window.onload = function() {
var
// Get the svg element we want to be able to change
svgElement = document.getElementById("checkSVG"),
// Get the use element that the svg uses
useElement = document.getElementById("checkUse"),
// Set a toggle bool to know if it should show a check
isChecked = true;
// Make sure the elements exist before we attach listeners
if (svgElement && useElement) {
// Whenever it gets clicked, change it's <use>
svgElement.addEventListener("click", function() {
// If it is checked, make it a check
if (isChecked)
useElement.setAttribute("xlink:href", "#checked-icon");
// Otherwise, no check
else
useElement.setAttribute("xlink:href", "#unchecked-icon");
// Toggle the state
isChecked = !isChecked;
});
}
};
}
else {
// Wait for the document to load
$(document).ready(function() {
// Set a bool for toggling the state
var isChecked = true;
// Add a listener for clicking on the SVG
$("#checkSVG").click(function() {
// If it is checked, make it a check
if (isChecked)
$("#checkUse").attr("xlink:href", "#checked-icon");
// Otherwise, no check
else
$("#checkUse").attr("xlink:href", "#unchecked-icon");
// Toggle state
isChecked = !isChecked;
});
});
}
CSS
css/index.css
#header{
font-family: Arial;
font-size: 2em;
}
#myGraphic{
width: 0;
height: 0;
}
#checkSVG{
width: 200px;
}
HTML
index.html
<!DOCTYPE html>
<html>
<head>
<title>Vector Graphic Changer</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="css/index.css">
<script src="js/libs/jquery/jquery.js"></script>
<script src="js/index.js"></script>
</head>
<body>
<!--The Definition of the Graphics-->
<svg id="myGraphic">
<g id="unchecked-icon">
<path d="M22.215,44.176c-12.11,0-21.963-9.851-21.963-21.962c0-12.11,9.852-21.962,21.963-21.962 c12.11,0,21.961,9.852,21.961,21.962C44.176,34.325,34.325,44.176,22.215,44.176z M22.215,2.557 c-10.839,0-19.658,8.818-19.658,19.657s8.818,19.658,19.658,19.658c10.839,0,19.657-8.818,19.657-19.658S33.054,2.557,22.215,2.557 z" />
</g>
<g id="checked-icon">
<path d="M22.215,44.176c-12.11,0-21.963-9.851-21.963-21.962c0-12.11,9.852-21.962,21.963-21.962 c12.11,0,21.961,9.852,21.961,21.962C44.176,34.325,34.325,44.176,22.215,44.176z M22.215,2.557 c-10.839,0-19.658,8.818-19.658,19.657s8.818,19.658,19.658,19.658c10.839,0,19.657-8.818,19.657-19.658S33.054,2.557,22.215,2.557 z" />
<polygon points="20.337,32.279 12.274,24.947 14.642,22.344 19.745,26.985 30.005,12.311 32.888,14.327" />
</g>
</svg>
<!--Header-->
<div id="header">My Graphic</div>
<!--The Graphic being used-->
<svg id="checkSVG" viewBox="0 0 44.429 44.429">
<use id="checkUse" xlink:href="#unchecked-icon"></use>
</svg>
</body>
</html>
I'm attempting to reorder DOM SVG elements using the native drag and drop events. The below code seems to work (with some strange image effects) in Firefox, work a limited number of times in Chrome (2 or 3 drag/drop reorderings work, then it seems to hang), and not very well at all in IE. My best guess is that there something about the events in question that I'm not thinking about correctly, some type of reset. Or perhaps using the drag events without dataTransfer this way is incorrect. My goal is to understand this type of function without libraries, but to have a clearer understanding of DOM functions, JavaScript, HTML, and CSS at the most basic level. I could easily be wrong anywhere in that list.
<!DOCTYPE html>
<html>
<head>
<title>Drag and Drop Experiments</title>
<style>svg { border-width:3px} </style>
</head>
<body>
<div id="main">
<svg id="s1" draggable="yes" width="100" height="100">
<circle cx="50" cy="50" r="30" fill="blue"></circle>
</svg>
<svg id="s2" draggable="yes" width="100" height="100">
<circle cx="50" cy="50" r="30" fill="red"></circle>
</svg>
<svg id="s3" draggable="yes" width="100" height="100">
<circle cx="50" cy="50" r="30" fill="yellow"></circle>
</svg>
</div>
<script type="text/javascript">
var dragSourceElement = null;
var dragTargetElement = null;
function doDragStart(e){
this.style.opacity = "0.4";
this.style.border = "solid";
dragSourceElement = this;
}
function doDragEnter(e){
if(dragSourceElement != this){
this.style.border = "dashed";
}
}
function doDragLeave(e){
if(dragSourceElement != this){
this.style.border = "";
}
}
function doDragOver(e){
if(dragSourceElement != this){
dragTargetElement = this;
e.preventDefault();//to allow a drop?
}
}
function doDragEnd(e){
this.style.border = "";
this.style.opacity = "1.0";
}
function doDragDrop(e){
if(dragSourceElement != dragTargetElement){
dnd_svg(dragSourceElement,dragTargetElement);
}
dragSourceElement.style.border = "";
dragTargetElement.style.border = "";
dragSourceElement.style.opacity = "";
dragSourceElement = null;
dragTargetElement = null;
}
//called after a drag and drop
//to insert svg element c1 before c2 in the DOM
//subtree of the parent of c2, assuming c1 is
//dropped onto c2
function dnd_svg(c1,c2){
var parent_c2 = c2.parentElement;
parent_c2.insertBefore(c1,c2);
}
function addL(n){
n.addEventListener('dragstart',doDragStart,false);
n.addEventListener('dragenter',doDragEnter,false);
n.addEventListener('dragleave',doDragLeave,false);
n.addEventListener('dragover',doDragOver,false);
n.addEventListener('drop',doDragDrop,false);
}
addL(document.getElementById("s1"));
addL(document.getElementById("s2"));
addL(document.getElementById("s3"));
</script>
</body>
</html>
This tutorial is pretty good for understanding native drag and drop, with lots of examples.
However, there might be particular problems associated to SVG. See for example this Mozilla bug: "the dragstart event is not fired on a svg element".
See this JSFiddle. There is an issue with drag and drop events on SVG's in FF, so one solution is to add a wrapper element to the SVG. With this, you can use the dataTransfer method and then append the node to the drop location.
<!DOCTYPE HTML>
<html>
<head>
<style type="text/css">
#div1,#div2 {
width:350px;
height:70px;
padding:10px;
border:1px solid #aaaaaa;
}
</style>
<script>
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
var id = ev.target ? ev.target.id : ev.srcElement.id;
if (id === "circle") {
id = ev.target.parentNode.parentNode.id;
}
ev.dataTransfer.setData("Text", id);
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("Text");
ev.target.appendChild(document.getElementById(data));
}
</script>
</head>
<body>
<div id="div1" ondrop="drop(event)"
ondragover="allowDrop(event)"></div>
<div id="div2" ondrop="drop(event)"
ondragover="allowDrop(event)"></div>
<br>
<div id="wrapper" draggable="yes" ondragstart="drag(event)" style="width:100px; height:100px;">
<svg id="svg" width="100" height="100" draggable="yes" ondragstart="drag(event)" >
<circle id="circle" cx="50" cy="50" r="30" fill="yellow"></circle>
</svg>
</div>
</body>
</html>
This works for me in both FF 18 and IE 9. The annoying part is that FF and IE both source different targets for these events, with IE returning the SVG as the target for the drag event, whereas FF returns the circle. Very annoying.
Update: I tried this JSFiddle in Chrome 24 and it works.