Currently I create a small modeling tool using Snap SVG.
var p = Snap.path(pathString).attr({fill:'none', stroke:'black', strokeWidth:1});
creates connections between other elements.
Every element, including the paths are clickable. Since it is hard to click exactly the path I tried to create an invisible outline around the path using Snap.filter.shadow so that a click event could be triggered for the path:
var filter = this.paper.filter(Snap.filter.shadow(2,2,1));
filter.click(function() {
do_as_if_i_clicked_the_path();
});
p.attr('filter', filter);
But adding a click event to the filter doesn't work.
Is there any way to create an invisible outline that is connected to the path so I can add events on it?
One possibility that works quite well, is to clone the object you want to have an event on, and either scale it up, or make the strokeWidth larger, and then have a very low opacity (you can increase this just for testing and then reduce it to almost invisible).
If you add it to a group like this example, you can just put a drag on the group, so no need to remove it or anything (unless you need the clone not to be there sometimes, which makes sense).
jsfiddle
var s = Snap("#svg");
var myLine = s.path('M0,0L100,100').attr({ stroke: 'black', strokeWidth: 1 });
var myClone = myLine.clone().attr({ strokeWidth: 50, opacity: 0.01 });
var myGroup = s.g( myLine, myClone );
myGroup.drag();
Related
I need to move object between layers, actually I have a main working area (say shape of square) and inside this working area I have a similar shape (bleed shape) same of the outer one but smaller in size, something like this :
https://jsfiddle.net/p0hszz22/24/ [see circle, square, rectangle shapes]
//# add main square
function addSquare() {
clearThisCanvas();
var square = new fabric.Rect({});
canvas.add(square);
addSquare1(); //call bleedline square
}
//# add bleedline square
function addSquare1() {
var square = new fabric.Rect({});
canvas.add(square);
centreOnCanvas(square);
}
So I got two objects on canvas, now my requirement is when I add any image and move that image(or any object) inside the working area the dotted line(bleed shape) should always be on above of my image, so that it shows the user hint that images or anything outside the bleed region will be ignored (present case : does not happened at all )
What I have tried :
I tried to change the order of these two shapes,and when I did the bleed shape hides itself when main shape(outer one) is selected.
I thought of making these shapes into a group but again no success.
How can I achieve this? all I need is to show the user as soon anything crosses the bleed line, it will not be considered.
Any link to study or keywords that I can again look for or any suggestion or any example will be helpful.
Thanks
PS: sorry for code in fiddle actually it was just to setup things quickly.
Try using an overlay image for the bleeding shape, maybe that fit your needs.
That image will always be on top of the other objects inside the canvas
canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png',
canvas.renderAll.bind(canvas), {
opacity: 0.5,
width : 400, // your width
height : 400, // your height
originX: 'center',
originY: 'center'
});
For example:
one tap is moving a shape.
Another tap is moving another shape at same time.
I have shared a small experiment I did on Kinetic + Multi Touch here. Have a look - perhaps it can help until "real" multitouch becomes available in Kinetic
You can set up a Kinetic Group like this:
http://www.html5canvastutorials.com/kineticjs/html5-canvas-complex-shapes-using-groups-with-kineticjs/
Here's how to declare an empty group and make it draggable:
var group = new Kinetic.Group({
x: 220,
y: 40,
draggable:true
});
So in the tap handler of a shape, you can add the tapped object to the group.
circle.on('tap', function() {
group.add(circle);
});
Then you can drag the assembled group as necessary.
And if you need multiple groups, you can move individual shapes between the different groups like this:
http://www.html5canvastutorials.com/kineticjs/html5-canvas-move-shape-to-another-container-with-kineticjs/
circle.moveTo(someOtherGroup);
In the case of dragging two shapes at the same time, you will have to grab the touch event from the browser and read it as
touches[0] and touches[1] events
This is how you differentiate between separate touch events occurring simultaneously
official kineticjs example:
http://www.html5canvastutorials.com/labs/html5-canvas-multi-touch-scale-stage-with-kineticjs/
I'm having a play around with Kinetic JS at the moment, with the following example:
http://jsfiddle.net/r8rtJ/4/
var yoda = new Kinetic.Image({
x: 0,
y: 0,
image: imageObj,
width: 106,
height: 118,
scale: 2
});
poly.setFill({
image: imageObj,
offset: {x: 0, y: 0}
});
And i'm wondering if there is a way to fade in the opacity of the image fill once the object has loaded? At the moment it just applies to the polygon instantly, and i can't seem to figure out how to set the opacity of the fill independently from the object itself.
Cheers!
setFill merely tells the polygon object what it should fill itself with when it is supposed to draw itself. This can be image or color.
To set the opacity with which the polygon is drawn, you have the opacity property. What you're trying to achieve can be done by combining a timeout with a function which increases the polygon's opacity and redraws it.
var fadeIn = function(shape) {
var o = shape.getOpacity();
o = o + 0.1 >=1 ? 1 : o + 0.1;
shape.setOpacity(o);
shape.getLayer().draw();
if(o !== 1) {
setTimeout(function() {
fadeIn(shape);
}, 120);
}
};
Example.
EDIT:
There seems to be no way to set the opacity of the fill currently in KinectJS. I've been trying to see if I can find a workaround, though.
One way to do it is, as you mentioned, drawing another polygon with only the stroke and an empty fill over the polygon with the fill, and removing the stroke polygon once the fading in has finished.
Example.
Another way would be to change the drawFunc for the particular polygon.
Whenever a particular layer's draw function is called, drawScene and drawHit functions are called for all its children. drawScene and drawHit functions for Kinetic.Shape call the object's drawFunc. drawScene sets the globalAlpha based on the shape's opacity before calling drawFunc.
The way drawFunc for Kinetic.Polygon is set up is that first, a path is formed on the context for the sides of the polygon. Then it is filled, followed by stroke. The way filling is done is simply setting the fillStyle of the context based on the fill of the polygon, and then context.fill is called.
How is the fill style set with images? When setFill is called for a polygon and the object passed as argument has the property image, fillType is considered to be 'PATTERN'. When the path made by drawFunc is to be filled, a pattern is created using the image and context.createPattern and set as the fill style. Opacity is never considered.
But, we can make a particular polygon consider the fill's opacity by changing its drawFunc with setDrawFunc. I simply copied the drawFunc from KinectJS source and added a condition to fill patterns differently by considering opacity passed with setFill.
Example.
Note that, though, this breaks the mouseover event for some reason. It'd be best to stick to method one until I, or someone else, for that matter, figures out what makes the mouseover not work when drawFunc is changed.
EDIT 2:
I managed to figure it out!
A polygon's drawHitFunc is the same as its drawFunc. So when we change the drawFunc, we end up changing the drawHitFunc which creates problems for when hit detection is required - as is the case with mouseover events.
So, when we change the drawFunc for a polygon, if we also change the drawHitFunc with setDrawHitFunc so that it is the same as the original drawFunc, we avoid breaking the hit-detection.
Example.
yes there's a way to do this. use the transitionTo method to fade the shape opacity like this:
http://www.html5canvastutorials.com/kineticjs/html5-canvas-linear-transition-tutorial-with-kineticjs/
If you want to have a stroke applied, that stays opaque the whole time, you can simply create another shape with a stroke only that sits on top of the shape filled with an image
I'm trying to build a web app using KineticJS, I basically have a function which runs on load which adds an image to a canvas (stage in KineticJS). Each time the function runs though, it is adding the image again underneath the existing one, where as I need it to replace the one that is currently on the stage. I know it's something to do with me creating a new image object each time the function is loaded, but I'm confused as to why it's positioning is different (underneath). I have simulated the same event here: http://jsfiddle.net/UCvbe/ - If you click the button several times, the image duplicates below. The final function will be ran when a user selects a knocker, therefore the final function must accept a parameter eg 'silver' so that it knows to draw the silver knocker etc.
Any help will be greatly appreciated!
Your code looks like this:
$('#testBtn').click(function() {
var stageKnocker = new Kinetic.Stage({
container: "canvas-overlay",
width: 402,
height: 470
});
var layerKnocker = new Kinetic.Layer();
var imageObj = new Image();
imageObj.onload = function(){
var image = new Kinetic.Image({
x: 193,
y: 110,
image: imageObj,
width: 25,
height: 50,
draggable: true
});
layerKnocker.add(image);
stageKnocker.add(layerKnocker);
};
imageObj.src = "http://shop.windowrepairshop.co.uk/WebRoot/Store3/Shops/es115683_shop/49E6/ECF2/0C60/3750/10B1/50ED/8970/710D/V6GP_0020_gold_0020_plate_0020_vic_0020_urn_0020_LArge.jpg";
})
The first problem is that you are creating a new Stage each time a user clicks the button. Don't do that--you only want one stage.
You're also creating a new layer each time a user clicks the button. Don't do that. You only need & want a single layer.
Finally, you are creating a new Kinetic.Image each time a user clicks the button and adding it to the layer. That's fine! Keep doing that. But you also want to remove the old Kinetic.Image. If you don't, you will just keep adding images, not replacing them.
So before adding the Kinetic.Image to the layer, you want to remove any existing images from the layer: layerKnocker.removeChildren() will remove everything in the layer.
When you're done, you will have a single stage, a single layer, and at any point in time a single image.
Is there any trick to determine if user clicks on given element rendered in canvas? For example I'm displaying rhombus from .png file with transparent background and i want to know if user click inside or outside that figure (like mouse-element collision).
There is no concept of individual elements in a canvas - it is simply just an area that you're drawing pixels onto. SVG on the other hand is made up of elements which you can then bind events to. However there are a few approaches you can take to add click events to canvas:
Position an html element that overlays the area on the canvas you want to be clickable. A for a rectangular area or an image map for something more irregular.
Use separate canvases for each element that you want to be clickable.
CAKE - I haven't used this myself, but it's description is "SVG sans the XML". This may cover your needs. Demos here http://glimr.rubyforge.org/cake/canvas.html#EditableCurve
One idea is to draw the image to a temporary canvas, then use getImageDate() to receive data for the pixel you are interested in, and check if its alpha value is 0 ( = transparent).
The following is a sketch of a solution. It is assumed that...
x and y are the coordinates of the mouse click event
you are looping over gameObjects, the current object being stored in the variable gameObject
the game object has been initialized with an image, x and y coordinates
The following code would then check whether the click was on a transparent area:
var tempCanvas = document.createElement('canvas');
if (tempCanvas.getContext) {
tempContext = tempCanvas.getContext('2d');
}
tempContext.drawImage(gameObject.img, 0, 0);
var imgd = tempContext.getImageData(x - gameObject.x, y - gameObject.y, 1, 1);
var pix = imgd.data;
if (pix[3] == 0) {
// user clicked on transparent part of the image!
}
Note: This is probably quite inefficient. I'm sure someone can come up with a better solution.
I have solve this problem using kintech.js, tutorials and examples can be found: http://www.html5canvastutorials.com/kineticjs/html5-canvas-drag-and-drop-tutorial/