Creating horizontal linear gradient with Raphael - javascript

Im new to the Raphael library (looks great so far btw)
I was wondering how to create a horizontal linear gradient.
Here's my test code so far, mostly based from examples I've been looking at:-
$(function () {
var paper = Raphael(0, 0, $(window).width(), $(window).height());
var path = paper.path("M800,100 L800,600 Q801,610 802,600 T803,600 L803,100 Q802,110 801,100 T800,100").attr({
"fill": "90-#f00:5-#00f:100",
"fill-opacity": 0.5
});
var pathArray = path.attr("path");
handle = paper.circle(pathArray[0][1], 350, 5).attr({
fill: "black",
cursor: "pointer",
"stroke-width": 1,
stroke: "transparent"
});
var start = function () {
this.cx = this.attr("cx"),
this.cy = this.attr("cy");
},
move = function (dx, dy) {
var X = this.cx + dx, Y = this.cy + dy;
this.attr({ cx: X, cy: Y });
pathArray[0][1] = pathArray[1][1] = pathArray[6][1] = X;
path.attr({ path: pathArray });
},
up = function () {
this.dx = this.dy = 0;
};
handle.drag(move, start, up);
});
I see from the SVG spec on the w3 site there is an x1,x2,y1,y2 attribute in the actual linearGradient tag (although I'm not even sure if they handle orientation? http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement).
I just haven't used Raphael enough to know how to set that in my path attribute.
Cheers,
wacka.
P.S.
EDIT: Adding the following helps, but only works in IE:-
$("linearGradient").attr("x1", "0");
$("linearGradient").attr("x2", "100%");
$("linearGradient").attr("y1", "0");
$("linearGradient").attr("y2", "0");
ANOTHER EDIT: Interestingly, the above only worked in IE but the following works in both (even though the HTML is the same):-
$("defs").children().attr("x1", "0");
$("defs").children().attr("x2", "100%");
$("defs").children().attr("y1", "0");
$("defs").children().attr("y2", "0");
For some reason the following is 1 in IE and 0 in chrome:-
$("lineargradient").length
Now, although this works, surely there must be a nicer way?

Here's an example of a rect with a horizontal and a vertical gradient.
paper.rect(100,100,200,200).attr({"fill":"0-#f00:5-#00f:100"});
paper.rect(300,300,200,200).attr({"fill":"90-#f00:5-#00f:100"});
the first value in the fill is the angle of your gradient. You can apply that to your path.

Related

Need to add a reference axis for the fabricJS objects

Creating an editing tool using fabricJS.. I want to add a reference axis to help user to align the object when the object is getting rotated. I need something like what is in the picture->
Basically I want to know how to modify the selection to add those axis
*I don't know why people downvote without bothering to even give a probable solution. The question I have doesn't require a snippet neither the solution is available elsewhere!
This is how you can modify a default fabricjs function mantaining the original behaviour and adding something on top of it.
var img02URL = 'http://fabricjs.com/lib/pug.jpg';
var originalDrawBorders = fabric.Object.prototype.drawBorders;
fabric.Object.prototype.drawBorders = function(ctx, styleOverride) {
originalDrawBorders.call(this, ctx, styleOverride);
var wh = this._calculateCurrentDimensions(),
strokeWidth = 1 / this.borderScaleFactor,
width = wh.x + strokeWidth + 40,
height = wh.y + strokeWidth + 40;
ctx.strokeStyle = 'red';
ctx.beginPath();
ctx.moveTo(-width / 2, 0);
ctx.lineTo(width/ 2, 0);
ctx.moveTo(0, -height/2);
ctx.lineTo(0, height/2);
ctx.stroke();
}
var canvas = new fabric.Canvas('c');
canvas.setZoom(0.5)
fabric.Image.fromURL(img02URL, function(oImg) {
oImg.scale(.40);
oImg.left = 180;
oImg.top = 0;
canvas.add(oImg);
canvas.renderAll();
});
#c {
border:1px solid #ccc;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.17/fabric.min.js"></script>
<canvas id="c" width="400" height="400"></canvas>

Mousemove event won't fire on canvas even if z-index is max

I’m a bit at loss since a couple of weeks, trying to get a mousemove event to work on a set of layered canvases.
Following advice in an earlier post, I confirmed that the event triggers properly only when the z-index of the targeted layer is on top (as demonstrated by this simple fiddle):
However, in the extended code I’m working with (d3 parcoords), despite having the same HTML structure as above, I can’t get the event to fire for canvases in the parallel coordinates chart on top.
This bl.ocks shows the extended version, and how the event won't work even when the targeted layered canvas has the greatest z-index (although the event does work well in a simple canvas below the chart).
I tried making a minimal example out of the parcoords file, but can’t manage to get a useful and working version given the number of interconnected functions.
I’m hoping someone who knows the original parcoords code might be able to clarify how exactly the chart's canvases are organized, and if something particular there might be causing the mousemove event not to work. Alternatively, maybe some experienced eyes might catch something I’m missing in the examples I posted.
Any tips much appreciated!
Extract of code from d3.parcoords.js which generates the canvases:
var pc = function(selection) {
selection = pc.selection = d3.select(selection);
__.width = selection[0][0].clientWidth;
__.height = selection[0][0].clientHeight;
// canvas data layers
["marks", "foreground", "brushed", "highlight", "clickable_colors"].forEach(function(layer, i) {
canvas[layer] = selection
.append("canvas")
.attr({
id: layer, //added an id for easier selecting for mouse event
class: layer,
style: "position:absolute;z-index: " + i
})[0][0];
ctx[layer] = canvas[layer].getContext("2d");
});
// svg tick and brush layers
pc.svg = selection
.append("svg")
.attr("width", __.width)
.attr("height", __.height)
.style("font", "14px sans-serif")
.style("position", "absolute")
.append("svg:g")
.attr("transform", "translate(" + __.margin.left + "," + __.margin.top + ")");
return pc;
};
Function used to draw the squares and set the mousemove event:
//This custom function returns polyline ID on click, based on its HEX color in the hidden canvas "clickable_colors"
//Loosely based on http://jsfiddle.net/DV9Bw/1/ and https://stackoverflow.com/questions/6735470/get-pixel-color-from-canvas-on-mouseover
function getPolylineID() {
function findPos(obj) {
var curleft = 0, curtop = 0;
if (obj.offsetParent) {
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
} while (obj = obj.offsetParent);
return { x: curleft, y: curtop };
}
return undefined;
}
function rgbToHex(r, g, b) {
if (r > 255 || g > 255 || b > 255)
throw "Invalid color component";
return ((r << 16) | (g << 8) | b).toString(16);
}
// set up some squares
var my_clickable_canvas = document.getElementById('clickable_colors');
var context = my_clickable_canvas.getContext('2d');
context.fillStyle = "rgb(255,0,0)";
context.fillRect(0, 0, 50, 50);
context.fillStyle = "rgb(0,0,255)";
context.fillRect(55, 0, 50, 50);
$("#clickable_colors").mousemove(function(e) {
//$(document).mousemove(function(e) {
//debugger;
var pos = findPos(this);
var x = e.pageX - pos.x;
//console.log(x)
var y = e.pageY - pos.y;
var coord = "x=" + x + ", y=" + y;
var c = this.getContext('2d');
var p = c.getImageData(x, y, 1, 1).data;
var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
$('#status').html(coord + "<br>" + hex);
console.log("Polyline's hex:" + hex)
});
}
The svg is covering your canvases.
The canvases and svg are all on the same level, but you have not set a z-index on the svg, and as it is rendered after all the canvases.
Simply putting z-index: 0; on the svg in the page you linked fixed it for me in Chrome.
It seems to simply be a problem with z-indexes.
You should set both css position and z-index on all the canvases and the sgv on the same level.
EDIT
Sorry, I was wrong about it being just the z-index.
I could get it to work by removing the following css.
.parcoords > canvas {
pointer-events: none;
}
But that seems to be in the library you are using, so just override it.
.parcoords > canvas {
pointer-events: auto;
}

Animate dotted path, one dot after another

I'm using the latest version of Snap.svg to draw and animate a path within a SVG:
var s = Snap('#svg');
var getPath = s.path('M15 15L115 115');
var pathLength = getPath.getTotalLength();
getPath.attr({
stroke: '#000',
strokeWidth: 5,
strokeDasharray: pathLength + ' ' + pathLength,
strokeDashoffset: pathLength,
strokeLinecap: 'round'
}).animate({
strokeDashoffset: 0
}, 1500);
While this is working fine (as you can see here), I want to make it a dotted line, animated one dot after another.
I've built a quick prototype with circles (which you can see here), to illustrate the look and feel, but technically I want it to base on a custom path.
Basically I'm looking for a way to animate a dotted (complex) path; so a path with attributes would be as fine as circles on a path.
since stroke-dasharray can be an array of values you can leave the stroke-dashoffset at 0 and update the stroke-dasharray until you get to the complete line.
something like this although this example is not really smooth and optimized.
var s = Snap('#svg');
var getPath = s.path('M15 15L115 115');
var pathLength = getPath.getTotalLength();
var perc = 0;
var dotLength = 5;
var gapLength = 4;
getPath.attr({
stroke: '#000',
strokeWidth: 1,
strokeDasharray: 0,
strokeDashoffset: 0,
strokeLinecap: 'round'
});
function updateLine(){
perc +=1;
if(perc>100){
perc = 100;
}
var visibleLength = pathLength*perc/100;
var drawnLength = 0;
var cssValue = '';
while(drawnLength < visibleLength){
drawnLength += dotLength;
if(drawnLength < visibleLength){
cssValue += dotLength+ ' ';
drawnLength += gapLength;
if(drawnLength < visibleLength){
cssValue += gapLength+ ' ';
}
}else{
cssValue += (visibleLength + dotLength - drawnLength)+ ' ';
}
}
cssValue += pathLength;
if(perc<100){
setTimeout(updateLine, 100);
}
getPath.attr({
strokeDasharray: cssValue
});
}
updateLine();
http://jsfiddle.net/EEe69/7/
If you want the gaps to be "skipped" on the animation, you should substract them from the pathLength
Why not use d3?
There's an example doing something similar with a dotted line, based on mouse movement. I started a timed function to do what you're looking to do, but I think you can figure it out :)
Look at this part, and see if you can adjust it to do a specific path instead of d3.mouse:
d3.timer(function(step) {
var svgagain = d3.select("body").select("svg")
.on("mousemove", function() { var pt = d3.mouse(this); tick(pt); });
});

javascript how to move element on route on scroll

I have a route like a vertical snake. (like this http://www.my-favorite-coloring.net/Images/Large/Animals-Reptiles-Snake-31371.png )
How I can move element (circle 10x10) on route by X and Y position on scroll?
Horizonal is ok :
var coin = $('#coin');
$(window).scroll(function(){
var coinTop = coin.css('top'),
cointLeft = coin.css('left');
if($(window).scrollTop() > 100 && $(window).scrollTop() < 600){
$("#coin").css({ "left": contLeft + 'px' });
};
});
But how I cat move it smoothly along the route?
I would suggest using a vector (SVG) library, namely raphael.js for the animation part.
In raphael.js you can define a path and then animate any object along the length of that path.
Please see the example and a corresponding stackoverflow thread for better understanding:
http://raphaeljs.com/gear.html
How to animate a Raphael object along a path?
Compared to the thread, you need to attach the animation on the onScroll event, as opposed to attaching it to a Interval.
Edit:
Adding the relevant code snippet from the link, as the commenter suggested:
HTML:
<div id="holder"></div>
JS:
var e;
var myPath;
var animation; //make the variables global, so you can access them in the animation function
window.onload = function() {
var r = Raphael("holder", 620, 420),
discattr = {
fill: "#666",
stroke: "none"
};
function curve(x, y, zx, zy, colour) {
var ax = Math.floor(Math.random() * 200) + x;
var ay = Math.floor(Math.random() * 200) + (y - 100);
var bx = Math.floor(Math.random() * 200) + (zx - 200);
var by = Math.floor(Math.random() * 200) + (zy - 100);
e = r.image("http://openclipart.org/image/800px/svg_to_png/10310/Odysseus_boy.png", x, y, 10, 10);
var path = [["M", x, y], ["C", ax, ay, bx, by, zx, zy]];
myPath = r.path(path).attr({
stroke: colour,
"stroke-width": 2,
"stroke-linecap": "round",
"stroke-opacity": 0.2
});
controls = r.set(
r.circle(x, y, 5).attr(discattr), r.circle(zx, zy, 5).attr(discattr));
}
curve(100,100,200,300,"red");
animation = window.setInterval("animate()", 10); //execute the animation function all 10ms (change the value for another speed)
};
var counter = 0; // a counter that counts animation steps
function animate(){
if(myPath.getTotalLength() <= counter){ //break as soon as the total length is reached
clearInterval(animation);
return;
}
var pos = myPath.getPointAtLength(counter); //get the position (see Raphael docs)
e.attr({x: pos.x, y: pos.y}); //set the circle position
counter++; // count the step counter one up
};
Update:
I've recently used pathAnimator for the same task. Be careful about the performance though, a complicated animation might be quite intensive.

Raphael transform object diagonally and infinite setIntervals

I'm working on a small animation where the user drags a circle and the circle returns back to the starting point. I figured out a way to have the circle return to the starting point. The only problem is that it will hit one of the sides of the frame before returning. Is it possible for it to go straight back (follow the path of a line drawn between the shape and starting point).
The other problem is that my setInterval doesn't want to stop. If you try pulling it a second time it would pull it back before you release your mouse. It also seems to speed up after every time. I have tried using a while loop with a timer but the results weren't as good. Is this fixable?
var paper = Raphael(0, 0, 320, 200);
//var path = paper.path("M10 10L40 40").attr({stoke:'#000000'});
//var pathArray = path.attr("path");
var circle = paper.circle(50, 50, 20);
var newX;
var newY;
circle.attr("fill", "#f00");
circle.attr("stroke", "#fff");
var start = function () {
this.attr({cx: 50, cy: 50});
this.cx = this.attr("cx"),
this.cy = this.attr("cy");
},
move = function (dx, dy) {
var X = this.cx + dx,
Y = this.cy + dy;
this.attr({cx: X, cy: Y});
},
up = function () {
setInterval(function () {
if(circle.attr('cx') > 50){
circle.attr({cx : (circle.attr('cx') - 1)});
} else if (circle.attr('cx') < 50){
circle.attr({cx : (circle.attr('cx') + 1)});
}
if(circle.attr('cy') > 50){
circle.attr({cy : (circle.attr('cy') - 1)});
} else if (circle.attr('cy') < 50){
circle.attr({cy : (circle.attr('cy') + 1)});
}
path.attr({path: pathArray});
},2);
};
circle.drag(move, start, up);
Here's the Jfiddle: http://jsfiddle.net/Uznp2/
Thanks alot :D
I modified the "up" function to the one below
up = function () {
//starting x, y of circle to go back to
var interval = 1000;
var startingPointX = 50;
var startingPointY = 50;
var centerX = this.getBBox().x + (this.attr("r")/2);
var centerY = this.getBBox().y + (this.attr("r")/2);
var transX = (centerX - startingPointX) * -1;
var transY = (centerY - startingPointY) * -1;
this.animate({transform: "...T"+transX+", "+transY}, interval);
};
and the "start" function as follows:
var start = function () {
this.cx = this.attr("cx"),
this.cy = this.attr("cy");
}
Is this the behavior you are looking for? Sorry if I misunderstood the question.
If the circle need to get back to its initial position post drag, we can achieve that via simple animation using transform attribute.
// Assuming that (50,50) is the location the circle prior to drag-move (as seen in the code provided)
// The animation is set to execute in 1000 milliseconds, using the easing function of 'easeIn'.
up = function () {
circle.animate({transform: 'T50,50'}, 1000, 'easeIn');
};
Hope this helps.

Categories

Resources