Snap.svg change attr on hover using this - javascript

I'm drawing four rectangles on a Snap.svg canvas and want to change e.g. the opacity of the one the mouse cursor currently hovers over. I wanted one function to work for all those rects and thought there must be a way doing this using the keyword this but my code doesn't work and I don't understand why.
What am I doing wrong? This should be possible right?
var s = Snap(viewportWidth, viewportHeight);
var firstBox = s.rect(0, 0, 480, viewportHeight);
var secondBox = s.rect(480, 0, 480, viewportHeight);
var thirdBox = s.rect(960, 0, 480, viewportHeight);
var fourthBox = s.rect(1440, 0, 480, viewportHeight);
firstBox.attr({
fill: "lightblue",
id: "firstBox"
});
function hoverEffect() {
this.attr({
fill: "red"
});
}
$(this).on("hover", hoverEffect);

Related

Fabricjs - How to detect total width/height of canvas where objects exist

So I want to save the canvas based on scale 1 and I want to include all existing/visible objects.
So right now, my canvas is 600x400 and if I were to save it, it would only save what's inside that 600x400, and it respects zoom level (a higher zoom will see less things, a lower zoom will see more things but smaller).
I've gotten around this by running this code:
let zoom = this.canvas.getZoom();
this.canvas.setZoom(1);
let url = this.canvas.toDataURL({width: 1000, height: 700, multiplier: 1});
this.canvas.setZoom(zoom);
window.open(
url,
'_blank' // <- This is what makes it open in a new window.
);
What this does is save the image as 1000x700, so stuff that were past 600px but under 1000 still gets saved.
However, this is hard coded. So I was wondering if there was an existing function or a clean/simple way to detect where all the objects are in and returns the full size (width and height).
fiddle: http://jsfiddle.net/1pxceaLj/3/
Update 1:
var gr = new this.fabric.Group([], {
left: 0,
top: 0
});
this.canvas.forEachObject( (o) => {
gr.add(o);
});
this.canvas.add(gr);
this.canvas.renderAll();
console.log(gr.height, gr.width); // 0 0
Solution
Using group was the best idea. Best example: http://jsfiddle.net/softvar/mRA8Z/
function selectAllCanvasObjects(){
var objs = canvas.getObjects().map(function(o) {
return o.set('active', true);
});
var group = new fabric.Group(objs, {
originX: 'center',
originY: 'center'
});
canvas._activeObject = null;
canvas.setActiveGroup(group.setCoords()).renderAll();
console.log(canvas.getActiveGroup().height);
}
One possibility would be to create a kind of Container using a fabricjs group and adding all created objects to this container.
I updated your fiddle here: http://jsfiddle.net/1pxceaLj/4/
Thus, you could just use group.width and group.height, perhaps adding a little offset or minimum values, and there you are having dynamical value to pass into toDataUrl even when having a smaller canvas.
code:
var canvas = new fabric.Canvas('c');
var shadow = {
color: 'rgba(0,0,0,0.6)',
blur: 20,
offsetX: 10,
offsetY: 10,
opacity: 0.6,
fillShadow: true,
strokeShadow: true
}
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: "#FF0000",
stroke: "#000",
width: 100,
height: 100,
strokeWidth: 10,
opacity: .8
});
var rect2 = new fabric.Rect({
left: 800,
top: 100,
fill: "#FF0000",
stroke: "#000",
width: 100,
height: 100,
strokeWidth: 10,
opacity: .8
});
rect.setShadow(shadow);
//canvas.add(rect);
//canvas.add(rect2);
var gr = new fabric.Group([ rect, rect2 ], {
left: 0,
top: 0
});
canvas.add(gr);
function save()
{
alert(gr.width);
alert(gr.height);
let zoom = canvas.getZoom();
var minheight = 700;
canvas.setZoom(1);
let url = this.canvas.toDataURL({width: gr.width, height: gr.height > minheight ? gr.height : minheight, multiplier: 1});
canvas.setZoom(zoom);
window.open(
url,
'_blank'
);
}

Possible to override bounding box selection area in fabricjs - controls option

Here, we are working in fabric.js with creating design tool.We have create custom selection area for canvas object in fabric.js. I read source code in fabric.js, it generates square box for bounding box, but I want change to my custom. Selection area appearance. Can someone please advise me?
We want custom selection area appearance.
We have tried this code context.setLineDash() for selection area.
if (fabric.StaticCanvas.supports('setLineDash') === true) {
ctx.setLineDash([4, 4]);
}
Refer code from Github.But won`t working fine for my working area.
Here we have attached Borderdasharray Property creation in fabric function
fabric.Object.prototype.set({
borderColor: 'green',
cornerColor: 'purple',
cornerSize: 33,
transparentCorners: false,padding:4,
borderDashArray:[50,25]
});
But we need to create animated dancing dots/moving dots for that selection area in fabric.js.
How can we create custom selection area in fabric.js?
For an animated "borderDashArray", the MDN Documentation on lineDashOffset provides a Marching Ants example with the following (fiddle demo):
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var offset = 0;
function draw() {
  ctx.clearRect(0,0, canvas.width, canvas.height);
  ctx.setLineDash([4, 2]);
  ctx.lineDashOffset = -offset;
  ctx.strokeRect(10,10, 100, 100);
}
function march() {
  offset++;
  if (offset > 16) {
    offset = 0;
  }
  draw();
  setTimeout(march, 20);
}
march();
Based on fabricjs object_interactivity.mixin.js drawBorders function, the above can be applied as follows:
fabric.Object.prototype.set({
borderColor: 'green',
cornerColor: 'purple',
cornerSize: 33,
transparentCorners: false,padding:14,
borderDashArray:[50,25]
});
// initialize fabric canvas and assign to global windows object for debug
var canvas = window._canvas = new fabric.Canvas('c',{width: 500, height: 500});
textbox = new fabric.Textbox('Hello, World!', {
width: 200,
height: 200,
top: 80,
left: 80,
fontSize: 50,
textAlign: 'center',
});
canvas.add(textbox);
textbox2 = new fabric.Textbox('Hello, World!', {
width: 200,
height: 200,
top: 160,
left: 160,
fontSize: 50,
textAlign: 'center',
});
canvas.add(textbox2);
canvas.renderAll();
canvas.setActiveObject(textbox);
var offset = 0;
(function animate() {
offset++;
if (offset > 75) {
offset = 0;
}
canvas.contextContainer.lineDashOffset = -offset;
canvas.renderAll();
fabric.util.requestAnimFrame(animate);
})();
canvas.on('after:render', function() {
canvas.contextContainer.strokeStyle = 'green';
canvas.contextContainer.setLineDash([50,25]);
canvas.forEachObject(function(obj) {
var bound = obj.getBoundingRect();
canvas.contextContainer.strokeRect(
bound.left + 0.5,
bound.top + 0.5,
bound.width,
bound.height
);
})
});
fabricjs border animation fiddle demo.
For customization :
borderDashArray: Dash stroke of borders
cornerDashArray: Dash stroke of corners
cornerStrokeColor: If corners are filled, this property controls the color of the stroke
cornerStyle: standrd 'rect' or 'circle'
selectionBackgroundColor: add an opaque or transparent layer to the selected object.
For use this :
fabric.Object.prototype.set({
transparentCorners: false,
borderDashArray: ......

Raphael, filling background with image (fillfit)

I've been struggling with the following problem.
I have an element, which I'd like to fill with one image. Raphael is using pattern by its default settings, thus repeating the image.
I found http://xn--dahlstrm-t4a.net/svg/raphaeljs/fill-shape-with-image.html, which should do the task, but the same problem persists.
Any idea how to tweak the code such a way that you could set one image from upper left-corner to bottom right-corner?
case "fillfit":
var isURL = Str(value).match(R._ISURL);
if (isURL) {
el = $("pattern");
var ig = $("image");
console.log('R', R);
el.id = R.createUUID();
$(el, {x: 0, y: 0, height: 1, width: 1, "patternContentUnits": "objectBoundingBox"});
$(ig, {x: 0, y: 0, width: 1, height: 1, "preserveAspectRatio": "none", "xlink:href": isURL[1]});
el.appendChild(ig);
o.paper.defs.appendChild(el);
$(node, {fill: "url(#" + el.id + ")"});
o.pattern = el;
o.pattern && updatePosition(o);
}
break;
Yours
Heikki
The basis for this solution can be found here. I really wanted to incorporate this into a site I'm working on, but I didn't want to use a non-standard version of Raphael, and it also didn't seem like a good idea to cross my fingers and hope that the folks working on Raphael would incorporate the solution proposed by Dahlström.
This example is fully functional on its own.
<html>
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script>
$(function() {
var paper = Raphael(document.getElementById("paper"), 100, 100);
var circle = paper.circle(50, 50, 50);
var uuid = Raphael.createUUID();
var pattern = document.createElementNS("http://www.w3.org/2000/svg", "pattern");
var backgroundImage = paper.image("https://si0.twimg.com/profile_images/916160956/thumbnail_10502_bigger.jpg", 0, 0, 1, 1);
pattern.setAttribute("id", uuid);
pattern.setAttribute("x", 0);
pattern.setAttribute("y", 0);
pattern.setAttribute("height", 1);
pattern.setAttribute("width", 1);
pattern.setAttribute("patternContentUnits", "objectBoundingBox");
$(backgroundImage.node).appendTo(pattern);
$(pattern).appendTo(paper.defs);
$(circle.node).attr("fill", "url(#" + pattern.id + ")");
});
</script>
</head>
<body>
<div id="paper"></div>
</body>
</html>

Create dynamic rectangle/box using easelJs or KineticJs

Hi I am looking to make a floor plan editor (something like MsPaint) using JavaScript. I have shortlisted EaselJS or KinetiJS as my preferred libraries.
I would like to know how to create a dynamic rectangular box/line with these libraries. I intend to draw a rectangle by clicking the mouse and dragging it (while the mouse button remains pressed). Thus the size of the rectangle will depend on how far the mouse is dragged.
Any help will be appreciated. If anyone feels that any other library like fabrisJs or paperJs will be better alternative then I am open to solutions in these libraries as well.
Ok... by trial and error and lots of googling and reusing net code I got the answer for KineticJs.
Heres the complete solution:
http://jsfiddle.net/sandeepy02/8kGVD/
<html>
<head>
<script>
window.onload = function() {
layer = new Kinetic.Layer();
stage = new Kinetic.Stage({
container: "container",
width: 320,
height: 320
});
background = new Kinetic.Rect({
x: 0,
y: 0,
width: stage.getWidth(),
height: stage.getHeight(),
fill: "white"
});
layer.add(background);
stage.add(layer);
moving = false;
stage.on("mousedown", function(){
if (moving){
moving = false;layer.draw();
} else {
var mousePos = stage.getMousePosition();
rect= new Kinetic.Rect({
x: 22,
y: 7,
width: 0,
height: 0,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
layer.add(rect);
//start point and end point are the same
rect.setX(mousePos.x);
rect.setY(mousePos.y);
rect.setWidth(0);
rect.setHeight(0);
moving = true;
layer.drawScene();
}
});
stage.on("mousemove", function(){
if (moving) {
var mousePos = stage.getMousePosition();
var x = mousePos.x;
var y = mousePos.y;
rect.setWidth(mousePos.x-rect.getX());
rect.setHeight(mousePos.y-rect.getY());
moving = true;
layer.drawScene();
}
});
stage.on("mouseup", function(){
moving = false;
});
};
</script>
</head>
<body>
<div id="container" ></div>
</body>
</html>
​

Creating horizontal linear gradient with Raphael

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.

Categories

Resources