Snap SVG: Creating a scratch animation - javascript
I'm new to Snap SVG and I'm trying to make a pointer which scratches and make a trail of that scratch path. These are the few issues that I'm trying to fix:
The animation starts from the bottom instead of the top.
The pointer moves along the path and reaches the top and return back to where it started. It need not have to return to the origin.
Need some suggestions to animate the scratch path, it should appear only after the pointer passed through it.
Here is the code:
HTML
<svg id="svgout" width="550px" height="550px"></svg>
JS
var s = Snap("#svgout");
var scratchPathSVG = '<path class="cls-1" d="M224.58,215.86c-11.57,11.56-23.13,22.08-33.81,33.64C181,259,159.64,286.3,147.19,287.35c-26.69,1.05,0-32.59,7.12-41L189,202.19c21.35-27.33,43.59-53.62,64.94-79.9,9.79-12.62,23.13-24.18,26.69-41,0-2.1,0-4.21-.89-5.26C268.18,63.42,245.93,88.65,237,96c-49.82,44.15-97,94.62-144.12,144-6.23,6.31-28.47,22.08-31.14,31.54C64.45,260,85.8,239,92,231.63l64.05-82c10.68-13.67,69.39-65.18,61.38-88.31-.89-3.15-4.45-7.36-8-5.26-22.24,10.51-40,34.69-57.83,52.56-15.12,15.77-31.14,30.49-46.26,46.26C93.81,166.45,60,211.65,45.77,211.65c11.57,0,56.94-71.49,70.28-87.26C139.18,96,172.1,67.63,189,34c8.9-17.87,10.68-36.79-11.57-33.64C157,2.45,133,36.09,116.94,50.81c-33.81,31.54-66.72,63.08-98.75,95.67C7.51,157,22.64,174.86,33.31,164.35c36.48-36.79,73-73.59,111.2-108.28,10.68-10.51,23.13-18.92,33.81-30.49-.89-2.1-2.67-3.15-3.56-5.26-5.34,7.36-9.79,13.67-15.12,21-16,21-33.81,41-49.82,61C100,114.94,2.17,213.76,35.09,233.73c13.34,7.36,33.81-17.87,41.81-26.28,19.57-18.92,39.14-39.95,57.83-59.92,17.79-18.92,45.37-36.79,58.72-59.92,9.79-16.82,1.78-7.36-5.34,0-10.68,11.56-19.57,24.18-29.36,36.79-16,20-31.14,39.95-46.26,59.92C91.14,212.71,52.88,247.4,44,285.24c-.89,2.1,0,5.26,1.78,7.36.89,1.05,1.78,1.05,1.78,2.1,2.67,2.1,4.45,2.1,7.12,1.05,24.91-12.62,45.37-43.1,64.94-64.13,28.47-29.44,57.83-58.87,87.18-88.31,8.9-8.41,58.72-48.36,57.83-53.62.89,13.67-21.35,32.59-28.47,42.05-28.47,36.79-56.94,74.64-84.52,112.49-5.34,7.36-38.25,44.15-22.24,54.67,19.57,11.56,85.4-72.54,95.19-83.05.89,0,.89-1.05,0,0Z" />';
var pathToScratch = s.path("M224.58,215.86c-11.57,11.56-23.13,22.08-33.81,33.64C181,259,159.64,286.3,147.19,287.35c-26.69,1.05,0-32.59,7.12-41L189,202.19c21.35-27.33,43.59-53.62,64.94-79.9,9.79-12.62,23.13-24.18,26.69-41,0-2.1,0-4.21-.89-5.26C268.18,63.42,245.93,88.65,237,96c-49.82,44.15-97,94.62-144.12,144-6.23,6.31-28.47,22.08-31.14,31.54C64.45,260,85.8,239,92,231.63l64.05-82c10.68-13.67,69.39-65.18,61.38-88.31-.89-3.15-4.45-7.36-8-5.26-22.24,10.51-40,34.69-57.83,52.56-15.12,15.77-31.14,30.49-46.26,46.26C93.81,166.45,60,211.65,45.77,211.65c11.57,0,56.94-71.49,70.28-87.26C139.18,96,172.1,67.63,189,34c8.9-17.87,10.68-36.79-11.57-33.64C157,2.45,133,36.09,116.94,50.81c-33.81,31.54-66.72,63.08-98.75,95.67C7.51,157,22.64,174.86,33.31,164.35c36.48-36.79,73-73.59,111.2-108.28,10.68-10.51,23.13-18.92,33.81-30.49-.89-2.1-2.67-3.15-3.56-5.26-5.34,7.36-9.79,13.67-15.12,21-16,21-33.81,41-49.82,61C100,114.94,2.17,213.76,35.09,233.73c13.34,7.36,33.81-17.87,41.81-26.28,19.57-18.92,39.14-39.95,57.83-59.92,17.79-18.92,45.37-36.79,58.72-59.92,9.79-16.82,1.78-7.36-5.34,0-10.68,11.56-19.57,24.18-29.36,36.79-16,20-31.14,39.95-46.26,59.92C91.14,212.71,52.88,247.4,44,285.24c-.89,2.1,0,5.26,1.78,7.36.89,1.05,1.78,1.05,1.78,2.1,2.67,2.1,4.45,2.1,7.12,1.05,24.91-12.62,45.37-43.1,64.94-64.13,28.47-29.44,57.83-58.87,87.18-88.31,8.9-8.41,58.72-48.36,57.83-53.62.89,13.67-21.35,32.59-28.47,42.05-28.47,36.79-56.94,74.64-84.52,112.49-5.34,7.36-38.25,44.15-22.24,54.67,19.57,11.56,85.4-72.54,95.19-83.05.89,0,.89-1.05,0,0Z").attr({ fill: "none", opacity: "0" });
var pointer = s.path("M26.4,182.43c.63.63,1.46.49,1.52-.31.1-1.56-10.56-23.52-11-25.81s0-4.6,2.15-5.7c2.92-1.53,4.54.06,5.82,2.26s6.34,12.55,8.43,17.22c3.09,6.89,10.38,26.09,12.14,26.09,2.75,0,1.41-4.18,1.41-4.18s-2.45-4.1-2.29-5.54a2.75,2.75,0,0,1,3-2.71c2,.35,5.39,2.8,7,5.48,2.63,4.47,9.56,23-7.72,36.29C29.26,239,18.67,227,14.25,221.79A75.28,75.28,0,0,1,3.42,205.33C.25,199-.13,198.64.11,195.81c.26-3.11,4.25-6,9.69.65.59.73,2.18-.18,1.19-1.58-1.53-2.15-5.34-7.65-1.7-10,4.47-2.93,6.42,2.74,8.63,4.16.51.33,1.53-.36,1.42-.86-.53-2.34-4.41-5.15-2.93-8.59a4.18,4.18,0,0,1,6.2-2C24,178.8,25.09,181.13,26.4,182.43Z").attr({ fill: "#666"});
pointer.drawAtPath( pathToScratch, 10000, );
function Drawing( svgString, transformString, timeBetweenDraws ) {
this.fragment = Snap.parse( svgString );
this.pathArray = this.fragment.selectAll('path');
this.group = s.g();
this.timeBetweenDraws = timeBetweenDraws;
};
Drawing.prototype.init = function( svgString, transformString ) {
this.group.clear();
this.currentPathIndex = 0;
};
Drawing.prototype.endReached = function() {
if( this.currentPathIndex >= this.pathArray.length ) {
return true;
};
};
Drawing.prototype.callOnFinished = function() {
}
Drawing.prototype.initDraw = function() {
this.init();
this.draw();
};
Drawing.prototype.quickDraw = function() {
this.init();
this.timeBetweenDraws = 0;
this.draw();
};
Drawing.prototype.draw = function() { // this is the main animation bit
if( this.endReached() ) {
if( this.callOnFinished ) {
this.callOnFinished();
return
};
};
var myPath = this.pathArray[ this.currentPathIndex ] ;
this.leng = myPath.getTotalLength();
this.group.append( myPath );
myPath.attr({
fill: 'none',
"stroke-dasharray": this.leng + " " + this.leng,
"stroke-dashoffset": this.leng
});
this.currentPathIndex++;
myPath.animate({"stroke-dashoffset": 0}, this.timeBetweenDraws, mina.easeout, this.draw.bind( this ) );
};
var myDrawing1 = new Drawing( scratchPathSVG, 't0, 0, s1.8', 800 );
myDrawing1.initDraw();
Js Fiddle
Any ideas /suggestions will be appreciated :)
Thanks in Advance.
Related
How to use Mina in Snap svg?
This is an animation code using Snap svg: var s = Snap(3000,3000); var circle7 = s.circle(130,90,5); var circle8 = s.circle(155,90,5); var circle9 = s.circle(180,90,5); var circle10 = s.circle(205,90,5); var circle11 = s.circle(230,90,5); var circle12 = s.circle(255,90,5); circle.attr({fill:"#ffffff",opacity:0}); circle1.attr({fill:"#ffffff",opacity:0}); circle2.attr({fill:"#ffffff",opacity:0}); circle3.attr({fill:"#ffffff",opacity:0}); circle4.attr({fill:"#ffffff",opacity:0}); circle5.attr({fill:"#ffffff",opacity:0}); circle6.attr({fill:"#ffffff",opacity:0}); circle7.attr({fill:"#ffffff",opacity:0}); circle8.attr({fill:"#ffffff",opacity:0}); circle9.attr({fill:"#ffffff",opacity:0}); circle10.attr({fill:"#ffffff",opacity:0}); circle11.attr({fill:"#ffffff",opacity:0}); circle12.attr({fill:"#ffffff",opacity:0}); circle.animate({fill:"#0000FF",opacity:1},4500); circle1.animate({fill:"#0000FF",opacity:1},4000); circle2.animate({fill:"#0000FF",opacity:1},3500); circle3.animate({fill:"#0000FF",opacity:1},3000); circle4.animate({fill:"#0000FF",opacity:1},2500); circle5.animate({fill:"#0000FF",opacity:1},2000); circle6.animate({fill:"#0000FF",opacity:1},1500); circle7.animate({fill:"#0000FF",opacity:1},4500); circle8.animate({fill:"#0000FF",opacity:1},5000); circle9.animate({fill:"#0000FF",opacity:1},5500); circle10.animate({fill:"#0000FF",opacity:1},6000); circle11.animate({fill:"#0000FF",opacity:1},6500); circle12.animate({fill:"#0000FF",opacity:1},7000); var cloud = s.image("D:/DigiMKey/login page_files/cloud.png", 0 , 260, 180, 125); cloud.attr({opacity:0,width:100,height:65}); cloud.animate({opacity:1,width:200,height:125},1000) var school =s.image("D:/DigiMKey/images/School-Icon.png", 265, 50, 100,100); school.attr({opacity:0,width:80,height:80}); school.animate({opacity:1,width:100,height:100},2000) I want to animate these objects using Mina so that they can be executed one after another. Objects should fade in one by one. how to do that? This code gives no errors, however they all come at once instead of one after another.
There is a callback in animate function, use it to call the next animation. var s = Snap(300,300), circle7 = s.circle(130,90,5).attr({fill:"#fff",opacity:0}), circle8 = s.circle(155,90,5).attr({fill:"#fff",opacity:0}), circle9 = s.circle(180,90,5).attr({fill:"#fff",opacity:0}), circle10 = s.circle(205,90,5).attr({fill:"#fff",opacity:0}), circle11 = s.circle(230,90,5).attr({fill:"#fff",opacity:0}), circle12 = s.circle(255,90,5).attr({fill:"#fff",opacity:0}), duration = 500, props = {fill:"#00f",opacity:1}; /* var anim = function() { circle.animate({fill:"#00f",opacity:1}, duration,mina.linear,anim1);} var anim1 = function() { circle1.animate({fill:"#00f",opacity:1},duration,mina.linear,anim2);} var anim2 = function() { circle2.animate({fill:"#00f",opacity:1},duration,mina.linear,anim3);} var anim3 = function() { circle3.animate({fill:"#00f",opacity:1},duration,mina.linear,anim4);} var anim4 = function() { circle4.animate({fill:"#00f",opacity:1},duration,mina.linear,anim5);} var anim5 = function() { circle5.animate({fill:"#00f",opacity:1},duration,mina.linear,anim6);} var anim6 = function() { circle6.animate({fill:"#00f",opacity:1},duration,mina.linear,anim7);} */ var anim7 = function() { circle7.animate(props,duration,mina.linear,anim8);} var anim8 = function() { circle8.animate(props,duration,mina.linear,anim9);} var anim9 = function() { circle9.animate(props,duration,mina.linear,anim10);} var anim10= function() { circle10.animate(props,duration,mina.linear,anim11);} var anim11= function() { circle11.animate(props,duration,mina.linear,anim12);} var anim12= function() { circle12.animate(props,duration,mina.linear);}; anim7(); <script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js"></script>
There is nothing there that makes them animate in a sequence. The way to animate in a sequence is to use a callback, which is part of the animate() method. So you can do... circle1.animate({ fill:"#0000FF" }, 1000, mina.linear, callbackFunc2 ); function callbackFunc2() { circle2.animate({ fill:"#0000FF" }, 1000, mina.linear, callbackFunc3 ); } function callbackFunc3()... And so on. Naturally that can feel a bit clunky when you have a lot, so I find it useful to add a sequence plugin to help. For example here function nextFrame ( el, frameArray, whichFrame ) { if( whichFrame >= frameArray.length ) { return } el.animate( frameArray[ whichFrame ].animation, frameArray[ whichFrame ].dur, nextFrame.bind( null, el, frameArray, whichFrame + 1 ) ); } I also extended it a bit and suggested it here (see the jsfiddle) which may be of use, if you're doing a lot of them.
rotating SVG rect around its center using vanilla JavaScript
This is not a duplicate of the other question. I found this talking about rotation about the center using XML, tried to implement the same using vanilla JavaScript like rotate(45, 60, 60) but did not work with me. The approach worked with me is the one in the snippet below, but found the rect not rotating exactly around its center, and it is moving little bit, the rect should start rotating upon the first click, and should stop at the second click, which is going fine with me. Any idea, why the item is moving, and how can I fix it. var NS="http://www.w3.org/2000/svg"; var SVG=function(el){ return document.createElementNS(NS,el); } var svg = SVG("svg"); svg.width='100%'; svg.height='100%'; document.body.appendChild(svg); class myRect { constructor(x,y,h,w,fill) { this.SVGObj= SVG('rect'); // document.createElementNS(NS,"rect"); self = this.SVGObj; self.x.baseVal.value=x; self.y.baseVal.value=y; self.width.baseVal.value=w; self.height.baseVal.value=h; self.style.fill=fill; self.onclick="click(evt)"; self.addEventListener("click",this,false); } } Object.defineProperty(myRect.prototype, "node", { get: function(){ return this.SVGObj;} }); Object.defineProperty(myRect.prototype, "CenterPoint", { get: function(){ var self = this.SVGObj; self.bbox = self.getBoundingClientRect(); // returned only after the item is drawn self.Pc = { x: self.bbox.left + self.bbox.width/2, y: self.bbox.top + self.bbox.height/2 }; return self.Pc; } }); myRect.prototype.handleEvent= function(evt){ self = evt.target; // this returns the `rect` element this.cntr = this.CenterPoint; // backup the origional center point Pc this.r =5; switch (evt.type){ case "click": if (typeof self.moving == 'undefined' || self.moving == false) self.moving = true; else self.moving = false; if(self.moving == true){ self.move = setInterval(()=>this.animate(),100); } else{ clearInterval(self.move); } break; default: break; } } myRect.prototype.step = function(x,y) { return svg.createSVGTransformFromMatrix(svg.createSVGMatrix().translate(x,y)); } myRect.prototype.rotate = function(r) { return svg.createSVGTransformFromMatrix(svg.createSVGMatrix().rotate(r)); } myRect.prototype.animate = function() { self = this.SVGObj; self.transform.baseVal.appendItem(this.step(this.cntr.x,this.cntr.y)); self.transform.baseVal.appendItem(this.rotate(this.r)); self.transform.baseVal.appendItem(this.step(-this.cntr.x,-this.cntr.y)); }; for (var i = 0; i < 10; i++) { var x = Math.random() * 100, y = Math.random() * 300; var r= new myRect(x,y,10,10,'#'+Math.round(0xffffff * Math.random()).toString(16)); svg.appendChild(r.node); } UPDATE I found the issue to be calculating the center point of the rect using the self.getBoundingClientRect() there is always 4px extra in each side, which means 8px extra in the width and 8px extra in the height, as well as both x and y are shifted by 4 px, I found this talking about the same, but neither setting self.setAttribute("display", "block"); or self.style.display = "block"; worked with me. So, now I've one of 2 options, either: Find a solution of the extra 4px in each side (i.e. 4px shifting of both x and y, and total 8px extra in both width and height), or calculating the mid-point using: self.Pc = { x: self.x.baseVal.value + self.width.baseVal.value/2, y: self.y.baseVal.value + self.height.baseVal.value/2 }; The second option (the other way of calculating the mid-point worked fine with me, as it is rect but if other shape is used, it is not the same way, I'll look for universal way to find the mid-point whatever the object is, i.e. looking for the first option, which is solving the self.getBoundingClientRect() issue.
Here we go… FIDDLE Some code for documentation here: let SVG = ((root) => { let ns = root.getAttribute('xmlns'); return { e (tag) { return document.createElementNS(ns, tag); }, add (e) { return root.appendChild(e) }, matrix () { return root.createSVGMatrix(); }, transform () { return root.createSVGTransformFromMatrix(this.matrix()); } } })(document.querySelector('svg.stage')); class Rectangle { constructor (x,y,w,h) { this.node = SVG.add(SVG.e('rect')); this.node.x.baseVal.value = x; this.node.y.baseVal.value = y; this.node.width.baseVal.value = w; this.node.height.baseVal.value = h; this.node.transform.baseVal.initialize(SVG.transform()); } rotate (gamma, x, y) { let t = this.node.transform.baseVal.getItem(0), m1 = SVG.matrix().translate(-x, -y), m2 = SVG.matrix().rotate(gamma), m3 = SVG.matrix().translate(x, y), mtr = t.matrix.multiply(m3).multiply(m2).multiply(m1); this.node.transform.baseVal.getItem(0).setMatrix(mtr); } }
Thanks #Philipp, Solving catching the SVG center can be done, by either of the following ways: Using .getBoundingClientRect() and adjusting the dimentions considering 4px are extra in each side, so the resulted numbers to be adjusted as: BoxC = self.getBoundingClientRect(); Pc = { x: (BoxC.left - 4) + (BoxC.width - 8)/2, y: (BoxC.top - 4) + (BoxC.height - 8)/2 }; or by: Catching the .(x/y).baseVal.value as: Pc = { x: self.x.baseVal.value + self.width.baseVal.value/2, y: self.y.baseVal.value + self.height.baseVal.value/2 }; Below a full running code: let ns="http://www.w3.org/2000/svg"; var root = document.createElementNS(ns, "svg"); root.style.width='100%'; root.style.height='100%'; root.style.backgroundColor = 'green'; document.body.appendChild(root); //let SVG = function() {}; // let SVG = new Object(); //let SVG = {}; class SVG {}; SVG.matrix = (()=> { return root.createSVGMatrix(); }); SVG.transform = (()=> { return root.createSVGTransformFromMatrix(SVG.matrix()); }); SVG.translate = ((x,y)=> { return SVG.matrix().translate(x,y) }); SVG.rotate = ((r)=> { return SVG.matrix().rotate(r); }); class Rectangle { constructor (x,y,w,h,fill) { this.node = document.createElementNS(ns, 'rect'); self = this.node; self.x.baseVal.value = x; self.y.baseVal.value = y; self.width.baseVal.value = w; self.height.baseVal.value = h; self.style.fill=fill; self.transform.baseVal.initialize(SVG.transform()); // to generate transform list this.transform = self.transform.baseVal.getItem(0), // to be able to read the matrix this.node.addEventListener("click",this,false); } } Object.defineProperty(Rectangle.prototype, "draw", { get: function(){ return this.node;} }); Object.defineProperty(Rectangle.prototype, "CenterPoint", { get: function(){ var self = this.node; self.bbox = self.getBoundingClientRect(); // There is 4px shift in each side self.bboxC = { x: (self.bbox.left - 4) + (self.bbox.width - 8)/2, y: (self.bbox.top - 4) + (self.bbox.height - 8)/2 }; // another option is: self.Pc = { x: self.x.baseVal.value + self.width.baseVal.value/2, y: self.y.baseVal.value + self.height.baseVal.value/2 }; return self.bboxC; // return self.Pc; // will give same output of bboxC } }); Rectangle.prototype.animate = function () { let move01 = SVG.translate(this.CenterPoint.x,this.CenterPoint.y), move02 = SVG.rotate(10), move03 = SVG.translate(-this.CenterPoint.x,-this.CenterPoint.y); movement = this.transform.matrix.multiply(move01).multiply(move02).multiply(move03); this.transform.setMatrix(movement); } Rectangle.prototype.handleEvent= function(evt){ self = evt.target; // this returns the `rect` element switch (evt.type){ case "click": if (typeof self.moving == 'undefined' || self.moving == false) self.moving = true; else self.moving = false; if(self.moving == true){ self.move = setInterval(()=>this.animate(),100); } else{ clearInterval(self.move); } break; default: break; } } for (var i = 0; i < 10; i++) { var x = Math.random() * 100, y = Math.random() * 300; var r= new Rectangle(x,y,10,10,'#'+Math.round(0xffffff * Math.random()).toString(16)); root.appendChild(r.draw); }
Using tween on class object, function definiton?
I am trying to do scene effects opening and closing scenes. But something is wrong with my self and this values. It says not defined or not working. How can ı define these functions. Ways I tried to define First way: var tween = createjs.Tween.get(this.scene).to({alpha : 0}, 5000).call(menuOutCompleted(nextScreen,isNextScreenPopUp)); It is throwing "menuOutCompleted is not defined"; Second way: var tween = createjs.Tween.get(this.scene).to({alpha : 0}, 5000).call(self.menuOutCompleted(nextScreen,isNextScreenPopUp)); It is not throwing any exception but does not do tween. It directly execute menuOutCompleted. Third way: var tween = createjs.Tween.get(this.scene).to({alpha : 0}, 5000).call(this.menuOutCompleted(nextScreen,isNextScreenPopUp)); It works like second way. My js class function CreditsScreen(SceneContainer) { var closePopUp_Button, background; this.name = "CreditsScreen"; var self = this; this.scene = new createjs.Container(); this.loadScreen = function() { background = new createjs.Shape(); background.graphics.beginBitmapFill(loader.getResult("coronaLogo")).drawRect(0,0,512,512); background.x = 200; background.y = 0; closePopUp_Button = new createjs.Sprite(buttonData, "exitIdle"); closePopUp_Button.framerate = 30; closePopUp_Button.x = 400; closePopUp_Button.y = 22; // play.addEventListener("click", handleClickPlay); this.scene.alpha = 0; this.scene.addChild(background); this.scene.addChild(closePopUp_Button); } this.menuIn = function() { console.log("menuIn CreditsScreen" ); stage.addChild(this.scene); //ptoblemetic part with self.menuInCompleted? var tween = createjs.Tween.get(this.scene).to({y : 0, x : 0, alpha : 1}, 5000).call(self.menuInCompleted); } this.menuInCompleted = function() { console.log("menuInCompleted CreditsScreen" ); self.addButtonEventListeners(); } this.menuOut = function(nextScreen,isNextScreenPopUp) { console.log("menuOut CreditsScreen" ); self.removeButtonEventListeners(); if(isNextScreenPopUp == true) { self.menuOutCompleted(nextScreen,isNextScreenPopUp); } else { //problematic part with menuInCompleted? var tweenSplash = createjs.Tween.get(this.scene).to({alpha : 0}, 5000).call(menuOutCompleted(nextScreen,isNextScreenPopUp)); } } this.menuOutCompleted = function(nextScreen,isNextScreenPopUp) { console.log("menuOutCompleted CreditsScreen" ); if (isNextScreenPopUp) { } else { stage.removeChild(this.scene); this.scene.x = 0; this.scene.y = 0; this.scene.alpha = 1; } changeMenu(nextScreen, this.name, isNextScreenPopUp, true); }
Ok. I solved the problem. Itis my call function. I send the parameters like menuoutcompleted(a,b) but in tween structure it must be (menuoutCompleted,[a,b]). Now, It works :)
Make JavaScript/jQuery tabs return to position on mouse out
This post is about the 3 green tabs at the top of the page (link is below). I have almost got these moving tabs how I want them, but I am not able to get the tabs to return to original position on mouse out or on a click elsewhere. Instead they move back to position after a set amount of time. How difficult is it to have them move back on mouse out? Site is here http://theveganproject.ca/wp/ Thanks! var sliding = 0; var slideTime = ''; // Set is sliding value function setSliding(a_ISliding){ sliding = a_ISliding; } // Get is sliding value function getSliding(){ return sliding; } // Carry out accordian styled effect function accordion(evt) { el = Event.element(evt); var eldown = getNextSibling(el); // If element is visible do nothing if ($('visible') == el) { return; } if ($('visible')) { if( getSliding() == 1 ){ return false; } var elup = getNextSibling($('visible')); setSliding( 1 ); parellelSlide( elup, eldown ); $('visible').id = ''; } else{ setSliding( 1 ); singleSlide( eldown ); } el.id = 'visible'; } // Setup accordian initial state function init() { var bodyPanels = document.getElementsByClassName('panel_body'); var panels = document.getElementsByClassName('panel'); var noPanels = panels.length; var percentageWidth = 100 / noPanels; var position = 0; // Loop through body panels and panels applying required styles and adding event listeners for (i = 0; i < bodyPanels.length; i++) { bodyPanels[i].hide(); panels[i].style.width = percentageWidth + '%'; panels[i].style.position = 'absolute'; panels[i].style.left = position + '%'; Event.observe(panels[i].getElementsByTagName('h3')[0], 'mouseover', accordion, false); Event.observe(panels[i].getElementsByTagName('h3')[0], 'mousemove', accordion, false); Event.observe(document.body, 'mousemove', resetIdle, false); position += percentageWidth; } if( $('visible') ){ // Set panel with id of visible to be initial displayed var vis = $('visible').parentNode.id+'-body'; $(vis).show(); } setIdle(); } // Next sibling method to work around firefox issues function getNextSibling(startBrother){ var endBrother=startBrother.nextSibling; while(endBrother.nodeType!=1){ endBrother = endBrother.nextSibling; } return endBrother; } function parellelSlide( elup, eldown ){ new Effect.Parallel( [ new Effect.SlideUp(elup), new Effect.SlideDown(eldown) ], { duration: 0.3, afterFinish: function() { setSliding( 0 );} }); } function singleSlide( eldown ){ new Effect.Parallel( [ new Effect.SlideDown(eldown) ], { duration: 0.3, afterFinish: function() { setSliding( 0 );} }); } function resetTabs(){ var resetEl = getNextSibling( $('visible') ); setSliding( 1 ); new Effect.Parallel( [ new Effect.SlideUp( resetEl ) ], { duration: 0.3, afterFinish: function() { setSliding( 0 );} }); $('visible').id = ''; } function resetIdle(){ if( $('visible') ){ window.clearTimeout( slideTime ); slideTime = window.setTimeout( "resetTabs()", 1000 ); } } function setIdle(){ if( $('visible') ){ slideTime = window.setTimeout( "resetTabs()", 1000 ); } } Event.observe(window, 'load', init, false);
Combining two control panels(AB) or toggle them (A) or (B)
I have two control panels; one is for the default draw features, the other for measure tools. The problem is that since they are in different panels it is possible to run two controls simultaneously, one from each panel. (this didn't seem like much of a problem before, but I noticed that when the measure and default draw tools are activated together, they cancel out the line end function) What I am trying to do is either place all controls in one panel and toggle them together or toggle the panels(e.g. when control from panel 1 is activated, deactivate all controls in panel 2) Here is my code: Default Controls Panel: OpenLayers.Control.CustomNavToolbar = OpenLayers.Class(OpenLayers.Control.Panel,{ initialize: function(options){ OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); this.addControls([ new OpenLayers.Control.Navigation({displayClass: 'olControlNavigation', zoomBoxEnabled:false}), new OpenLayers.Control.DrawFeature(vlayer, OpenLayers.Handler.Point, {displayClass: 'olControlDrawPoint'}), new OpenLayers.Control.DrawFeature(vlayer, OpenLayers.Handler.Path, {displayClass: 'olControlDrawPath'}), new OpenLayers.Control.DrawFeature(vlayer, OpenLayers.Handler.Polygon, {displayClass: 'olControlDrawPolygon'}), new OpenLayers.Control.ZoomBox({displayClass: 'olControlZoomBox', alwaysZoom:true}) ]) this.displayClass = 'olControlCustomNavToolbar' }, draw: function(){ var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments); this.defaultControl = this.controls[0]; return div; } }); var panel = new OpenLayers.Control.CustomNavToolbar({div:OpenLayers.Util.getElement('panel')}); map.addControl(panel); Measure Controls Panel: allControls = { line: new OpenLayers.Control.Measure(OpenLayers.Handler.Path, { persist: true, handlerOptions: { layerOptions: { renderers: renderer, styleMap: styleMap } }, textNodes: null, callbacks:{ create: function(){ this.textNodes = []; // vlayer.destroyFeatures(vlayer.features); mouseMovements = 0; }, modify: function(point, line){ if(mouseMovements++ < 5){ return; } var len = line.geometry.components.length; var from = line.geometry.components[len -2]; var to = line.geometry.components[len -1]; var ls = new OpenLayers.Geometry.LineString([from, to]); var dist = this.getBestLength(ls); if(!dist[0]){ return; } var total = this.getBestLength(line.geometry); var label = dist[0].toFixed(3) + " " + dist[1]; var textNode = this.textNodes[len -2] || null; if(textNode && !textNode.layer){ this.textNodes.pop(); textNode = null; } if(!textNode){ var c = ls.getCentroid(); textNode = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(c.x, c.y), {}, { label: "", fontColor: "#800517", fontSize: "13px", fontFamily: "Tahoma", fontWeight: "bold", labelAlign: "cm" }); this.textNodes.push(textNode); vlayer.addFeatures([textNode]); } textNode.geometry.x = (from.x + to.x) / 2; textNode.geometry.y = (from.y + to.y) / 2; textNode.style.label = label; textNode.layer.drawFeature(textNode); this.events.triggerEvent("measuredynamic", { measure: dist[0], total: total[0], units: dist[1], order: 1, geometry: ls }); } } }), polygon: new OpenLayers.Control.Measure( OpenLayers.Handler.Polygon, { persist: true, immediate: true, handlerOptions: { layerOptions: { renderers: renderer, styleMap: styleMap } } } ) }; var control; for(var key in allControls) { control = allControls[key]; control.events.on({ "measure": handleMeasurements, "measurepartial": handleMeasurements }); map.addControl(control); } ..and just for reference function handleMeasurements(evt){ var geometry = evt.geometry; var units = evt.units; var order = evt.order; var measure = evt.measure; var element = document.getElementById('output'); var position = (map.getLonLatPxFromViewPortPx); var out = ""; if(order == 1){ out += "Distance: " + measure.toFixed(3) + " " + units; } else{ out += "Area: " + measure.toFixed(3) + " " + units + "<sup>2</" + "sup>"; } element.innerHTML = out; } function toggleControl(element){ vlayer.destroyFeatures(vlayer.features); for(key in allControls){ var control = allControls[key]; if(element.value == key && element.click){ control.activate(); var label = document.getElementById('output'); var emptyOut = ""; label.innerHTML = emptyOut; } else{ control.deactivate(); } } } Been looking for a way to do this and so far have not been able to find anything useful, if you could help me with some suggestions about how to go about this it would be very appreciated. Thanks EDIT: I am not using GeoExt and am not considering using it. I'm looking for suggestions about how to go about this using only the OpenLayers 2.11 library. Thanks again.