Here is the "dragger" defined in the raphaeljs graffle example.
var dragger = function () {
this.ox = this.type == "rect" ? this.attr("x") : this.attr("cx");
this.oy = this.type == "rect" ? this.attr("y") : this.attr("cy");
this.animate({"fill-opacity": .2}, 500);
},
move = function (dx, dy) {
var att = this.type == "rect" ? {x: this.ox + dx, y: this.oy + dy} : {cx: this.ox + dx, cy: this.oy + dy};
this.attr(att);
for (var i = connections.length; i--;) {
r.connection(connections[i]);
}
r.safari();
},
up = function () {
this.animate({"fill-opacity": 0}, 500);
},
r = Raphael("holder", 640, 480),
connections = [],
shapes = [ r.ellipse(190, 100, 30, 20),
r.rect(290, 80, 60, 40, 10),
r.rect(290, 180, 60, 40, 2),
r.ellipse(450, 100, 20, 20)
];
Am I right that:
That move, up, r, connections and shapes are INDEPENDENT variables? I.e. NOT methods/ attributes of dragger?
This is just a sequential definition of variables at an equivalent scope?
Are move, up, etc. considered to have been declared using the var keyword?
Thanks for the help. Love and peace.
This is just like a long series of var statements.
Similar, and easier to read:
var a = 1, b = 2, c = 3;
In this case I definitely would NOT use this syntax, and would instead use a separate line for each variable. It's really hard to read as is.
In short: yes, you are right on both counts. This is exactly the same as
var dragger = ...;
var move = ...;
var up = ....;
...
except it's a pain to read. The extra commas hiding between declarations are what tie the whole thing together into the var statement.
Related
I'm new to d3 and a pretty average javascript programmer. I've got some code to create a circle, and a function to get the x and y co-ordinates of a point on a circle:
var innerCircle = svg.append('circle')
.attr({
cx: 100,
cy: 100,
r: 50,
'stroke': 'white',
'fill': 'transparent',
});
var pointOnCircle = function(circle, radians){
var cx = parseInt(circle.attr('cx'));
var cy = parseInt(circle.attr('cy'));
var r = parseInt(circle.attr('r'));
var x = cx + Math.sin(radians) * r;
var y = cy + Math.cos(radians) * r;
return {x: x, y: y};
}
It works. But I feel like continuing with this approach will make my code a messy grab bag of global functions, and that I should be able to make it object-oriented, so rather than calling:
var point = pointOnCircle(circle, Math.PI);
I can instead call:
var point = circle.pointAt(Math.PI);
But this would involve me either attaching a pointAt function to a d3 object somehow, or creating my own Circle object that has a pointAt function, and wraps a d3 object. Is either of these a good idea?
There's other points where I feel like I'd want something similar - kind of like I want to be mapping 'objects' to documents, as opposed to plain old data to documents. Is this a common requirement, or am I missing something conceptually?
What's the best way to approach my pointOnCircle issue that I'm having above?
Most of the d3 examples are small, self-contained, and written in one single script. Are there any examples showing how to build something with more reusable functions?
You can follow d3.js style of functional programming as demonstrated below
function innerCircle() {
var current_attr, current_style, circle_elem;
var _circle = function (svg) {
circle_elem = svg.append('circle')
.attr(current_attr)
.attr(current_style);
return circle_elem;
}
_circle.pointAt = function (randians) {
if(! circle_elem) //If the circle is not drawn yet.
return {x: -1, y: -1};
var cx = parseInt(circle_elem.attr('cx'));
var cy = parseInt(circle_elem.attr('cy'));
var r = parseInt(circle_elem.attr('r'));
var x = cx + Math.sin(radians) * r;
var y = cy + Math.cos(radians) * r;
return {x: x, y: y};
}
_circle.attr = function(attr_val){
if(! arguments.length)
return current_attr;
current_attr = attr_val;
return _circle;
}
_circle.style = function(style_val){
if(arguments.length == 1)
return current_style;
current_style = style_val;
return _circle;
}
return _circle;
}
This is a typical example of functional programming. The main object is _circle function is obtained by calling innerCircle. _circle draws a circle to an svg according to its set attributes (current_attr, current_style). To draw a circle to an svg, you can do it d3.js way:
var new_circle = innerCircle();
svg.call( new_circle );
The _circle function has 3 defined methods, attr, style and pointAt. attr and style are getter/setter functions, if you call them without arguments they will return the current value (getter), and if called with an argument, they will set the current value to it.
new_circle.style(); //get the current style
//set attributes
new_circle.attr({
cx: 100,
cy: 100,
r: 50,
'stroke': 'white',
'fill': 'transparent',
});
You can also call your pointAt function similarly.
new_circle.pointAt(Math.PI);
One last caveat to this programming style is the return _circle; statement at the end of all setter functions, which allows chaining. So, your example can be reproduced by:
var new_circle = innerCircle()
.attr({
cx: 100,
cy: 100,
r: 50,
})
.style({
'stroke': 'white',
'fill': 'transparent',
});
svg.call(new_circle);
Hope this helps. Let me know of any unclear points.
I hope this helps.
var myProgram = {};
myProgram.circleModule = (function() {
var innerCircle = d3.select("#svg").append('circle')
.attr({
cx: 100,
cy: 100,
r: 50,
'stroke': 'black',
'fill': 'red',
});
var pointOnCircle = function(circle, radians) {
var cx = parseInt(circle.attr('cx'));
var cy = parseInt(circle.attr('cy'));
var r = parseInt(circle.attr('r'));
var x = cx + Math.sin(radians) * r;
var y = cy + Math.cos(radians) * r;
return {
x: x,
y: y
};
}
return {
circle: innerCircle,
pointOnCircle: pointOnCircle
}
})();
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
</head>
<body>
<svg id="svg" width="200" height="200">
</svg>
Purpose
I'm making a simple "shoot the word" game, where user needs to click on some moving rectangles with words to "shoot" them.
Problem
So i create some objects and move them using simple kinetic.js tweening.
Word creation
function createWord(value){
//here comes some word object construction
var wordGroup = new Kinetic.Group({
x: 0,
y: 0
});
var padding = 10;
wordGroup.label = new Kinetic.Text({
x: padding,
y: padding,
text: value,
fontFamily: 'Times New Roman',
fontSize: 30,
fill: 'white'
});
wordGroup.tag = new Kinetic.Rect({
x: 0,
y: 0,
width: wordGroup.label.width() + (padding << 1),
height: wordGroup.label.height() + (padding << 1),
fill: 'black',
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: {x:10,y:20},
shadowOpacity: 0.5,
cornerRadius: 10
});
wordGroup.add(wordGroup.tag);
wordGroup.add(wordGroup.label);
wordGroup.shoot = function(){ //shooting mechanism (simple stop from moving and remove from scene)
wordGroup.tween.pause();
wordGroup.clean();
dropNextWord(); //drops fresh blood! (new word instead of shooted)
}
wordGroup.clean = function(){ //remove from scene and set it free to drop again
wordGroup.remove();
wordGroup.isActive = false;
}
wordGroup.move = function(callback){ //animates word
wordLayer.add(wordGroup);
moveToSide(wordGroup, callback); //calls moving function
}
wordGroup.on('click', function(e){
wordGroup.shoot();
});
return wordGroup;
}
Tweening part
//move word to opposite side
function moveToSide(word, callback){
var side = Math.random();
var d = 100;
spawnFromSide(word, side); //set random side word position
tweenPosition = {
x: word.x(),
y: word.y()
}
if(side < 0.25){ //left
tweenPosition.x = - d;
} else if(side > 0.25 && side < 0.5){ //right
tweenPosition.x = defaultStageWidth + d;
} else if(side > 0.5 && side < 0.75){ //up
tweenPosition.y = - d;
} else { //down
tweenPosition.y = defaultStageHeight + d;
}
word.tween = new Kinetic.Tween({
node: word,
duration: 4,
easing: Kinetic.Easings.Linear,
x: tweenPosition.x,
y: tweenPosition.y,
onFinish: function(){
word.clean();
callback();
}
});
word.tween.play();
}
But the problem is that click event doesn't fire on large amount of user clicks. As i think, this caused by delayed drawHit() calls inside tweening mechanism, that draws new object position before updating the hit area, so when we shoot object thinking that we hit its current position we miss because its hit area still have the same old position.
Live example
http://jsfiddle.net/hd6z21de/7/
Take a minute on shooting to see this effect in action
Solved this weird behavior by listening canvas touches and check if pointer collide some target word-rect by myself instead of using their own onclick events.
//i listen to canvas because of my app specific, you could simple listen your own layer or even document
$("canvas").bind('click', function(event){
var x = (event.pageX) / stage.scaleX(); //you don't need to divide by scale if your stage isn't scaled as mine does
var y = (event.pageY) / stage.scaleY();
var wordArray = wordGroup.getChildren();
for(var i = 0; i < wordArray.length; i++){ //go through all words and check if we shoot someone (is mouse position included in some word rect)
if(x > wordArray[i].x() &&
y > wordArray[i].y() &&
x < (wordArray[i].x() + wordArray[i].width()) &&
y < (wordArray[i].y() + wordArray[i].height())){
wordArray[i].shoot(); //shoot that word
break;
}
}
}
I made a simple "animation" with PhysicsJS, where I have this body:
balon = Physics.body('circle', {
x: 50,
y: random(20, 350),
vx: 0.45,
angle: random(0,360),
angularVelocity:-0.005,
radius: 60,
mass: 1,
fixed: true
});
balon.view = new Image();
balon.view.src = 'ballon.png';
All works good but I need to add a shadow for the "ball", this means that I need to use two images the "ballon.png" and the second image (the shadow) need to be fixed over the first image (don't rotate with the body).
Any idea hot to do this ?
Thank you in advance !
If you need one of the images to have a different behavior, you'll need to handle the rendering yourself.
You can add another rendering layer for shadows. If you store the shadow image inside body.shadow, then you can do something like this.
var shd = renderer.addLayer('shadows');
var bodies = [balon];
// draw the provided shadow view
shd.drawShadow = function( body, view ){
var pos = body.state.pos
,v = body.state.vel
,t = renderer._interpolateTime || 0
,x
,y
,aabb
,ctx = shd.ctx;
;
// interpolate positions
x = pos.x + v.x * t;
y = pos.y + + v.y * t;
ctx.save();
ctx.translate( x, y );
ctx.drawImage(view, -view.width/2, -view.height/2, view.width, view.height);
ctx.restore();
}
shd.render = function(){
var body;
for (var i = 0, l = bodies.length; i < l; i++){
body = bodies[ i ];
if ( body.shadow ){
shd.drawShadow( body, body.shadow );
}
}
};
I have a function which displays lines (x and y coordinates) based on the time information. The x and y coordinates specify the position of the drawn points whereas time represents the timestamps (in milliseconds) of the respective points.
Currently, there is a function which displays line as below
<script type="text/javascript" src="https://raw.github.com/DmitryBaranovskiy/raphael/master/raphael-min.js"></script>
<script type="text/javascript">
function drawLine(points) {
var paths = ['M ' + points[0].x + ' ' + points[0].y];
for (var i = 1; i < points.length; i++) {
var p = points[i];
paths.push(paths[i - 1] + ' L ' + p.x + ' ' + p.y);
}
var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
var line = paper.path(paths[0]);
var next = 1;
function animate() {
if (paths[next]) {
duration = points[next].t - points[next - 1].t
line.animate({ path: paths[next] }, duration, 'linear', animate);
next++;
}
}
animate();
}
</script>
And the function can be called using associative arrays as follows:
drawLine([
{ x: 0, y: 0, t: 0 },
{ x: 100, y: 230, t: 1520 },
{ x: 210, y: 290, t: 3850 },
{ x: 150, y: 200, t: 5060 },
]);
The question is, how can I modify this function to display points and not the lines?
You can add a drawPoint method, which will take an object with x and y properties
function drawPoint(point) {
paper.circle(point.x, point.y, 5).attr('fill', 'red');
};
Then call it from your animate function, before the points[next] comparison
drawPoint(points[next - 1]);
Here's the JSFiddle http://jsfiddle.net/jaimem/2krgN/
If you don't want the lines, then you don't need paths
function drawPoints(points){
var paper = new Raphael('canvas_container', 500, 500),
idx = 0;
function animate(){
if(points[idx]){
var currP = points[idx],
prevP = points[idx - 1],
d = currP.t - (prevP ? prevP.t : 0 );
paper.circle(currP.x, currP.y, 1)
.attr('fill', 'red')
.animate({r:5}, d, animate);
idx++
}
}
animate();
}
The recursive animate callback might be a little difficult to understand/read, so might just want to use a setTimeout. Also you can pass a string with the id of an element to the Raphael constructor and the library will find the DOM node for you.
JS Fiddle: http://jsfiddle.net/jaimem/Q5G5y/2/
I am new to Kineticjs and not a great javascript coder so I am hoping to get some help with this example.
http://jsfiddle.net/pwM8M/
I am trying to store the x axis on doors so when a redraw unrelated to the doors is done they don't go back to the default position. (multiple types of doors and windows too)
Each form element can have multiple quantities (more than one door) so I need a way to store/retrieve the data currently contained in the alert on jsfiddle.
I did some research but have come up empty. Can anyone help with what I have provided?
function OH(x,y,w,h) {
var oh = new Kinetic.Text({
x:x,
y: y,
width: w*scale,
height: h*scale,
stroke: wtc,
strokeWidth: 1,
fill: 'brown',
fontSize: 6,
fontFamily: 'Calibri',
padding:4,
text: w+' x '+h+' OH\n\n<- DRAG ->',
align: 'center',
textFill: 'white',
draggable: true,
dragConstraint:'horizontal',
dragBounds: {
left:xoffset, right: xoffset+width+length-(w*scale)
}
});
oh.on('dragend', function(e) {
alert(oh.getPosition().x);
});
window.south.add(oh);
}
Thanks,
I have fixed sized 40x40 rectangle here in which i am using dragging function. try this
var box = new Kinetic.Rect({
x: parseFloat(area.size.x),
y: parseFloat(area.size.y),
width: 40, //parseFloat(area.size.width)
height: 40,
fill: area.color,
stroke: 'black',
strokeWidth: 1,
opacity: 0.6,
id : area.id + id,
draggable: true,
dragBoundFunc: function(pos) {
// x
var newX = pos.x < 0 ? 40 : pos.x;
var newX = newX > _self.canvasWidth - area.size.width ? _self.canvasWidth - area.size.width : newX;
// y
var newY = pos.y < 0 ? 40 : pos.y;
var newY = newY > _self.canvasHeight - area.size.height ? _self.canvasHeight - area.size.height : newY;
return {
x: newX,
y: newY
};
Use this function
box.on('dragend', function() {
_self.draw = false;
_self.dragArea(area, box);
});
and try this to play with x y coordinates
dragArea: function(area, box){
if(box){
$$('#' + this.formId + ' [name="areas[' + area.id + '][size][x]"]').first().value = parseInt(box.attrs.x);
$$('#' + this.formId + ' [name="areas[' + area.id + '][size][y]"]').first().value = parseInt(box.attrs.y);
area.size.x = parseInt(box.attrs.x);
area.size.y = parseInt(box.attrs.y);
}
},
Create a new array before the function, like so:
var xArray = new Array();
then inside your function
oh.on('dragend', function(e) {
alert(oh.getPosition().x);
// ADD NEW ITEM TO ARRAY, STORE X POSITION
xArray.push(oh.getPosition().x);
});
and so all the x values get stored in the array.
If you need to clear the array, you can simply create a new one again with the same name.
And you can iterate through it with a loop if needed.
updated:
http://jsfiddle.net/pwM8M/2/