I have a canvas where using raphael I'm going to draw some images.
The images's names are in a list: images_list and I will draw three images for row. All the images have the same dimensions, are equaly spaced and are .png files (no vector).
Some example code:
var paper_images_list = Raphael(canvas_images_list, '100%', '100%');
var images_for_row=3
var y_max=Math.ceil(image_list.length/images_for_row)*40
paper_images_list.setSize(div_width, y_max);
for (var i=0; i<image_list.length; i++) {
var col=parseInt(i/images_for_row)
var y=col*40
var x=(i-images_for_row*col)*40
var image = paper_images_list.image('/media/images/'+image_list[i], x, y, 33, 27);
};
What I want: clicking on an image I want to know which image that is, for use it in another paper/canvas.
I'm using Raphael because I thinked it could make that easy but maybe I'm wrong. Is it possible to recognize the image just clicking over it?
If not, I can find the coordinates of the click and calculate which image there is, but I don't need Raphael for that, right? There are better solutions?
I'm using Phyton3.5, Django 1.9, Javascript/JQuery, Windows7
Thanks to Ian I found it's so simple like:
document.getElementById('canvas_images_list').onclick=function(event) {
var myimage = event.target
if (myimage.tagName == 'image') {
myimage = myimage.href.baseVal;
};
};
The if statement excludes the clicks outside any images
Related
Im using snap.svg an snap.svg.zpd libraries. Same issue I have if I use snap.svg and jQuery panzoom library combination.
Code sample you can find here.
var mySvg = $("#plan")[0];
var snap = Snap("#plan");
//create an image
var imagePlan = snap.image("http://upload.wikimedia.org/wikipedia/commons/4/42/Cathedral_schematic_plan_fr_vectorial.svg", 10, 10, 900, 500);
var group = snap.group(imagePlan);
snap.zpd();
var pt = mySvg.createSVGPoint(); // create the point;
imagePlan.click(function(evt)
{
console.log(evt);
pt.x = evt.x;
pt.y = evt.y;
console.log(mySvg.getScreenCTM().inverse());
//When click, create a rect
var transformed = pt.matrixTransform(mySvg.getScreenCTM().inverse());
var rect1 = snap.rect(transformed.x, transformed.y, 40, 40);
group.add(rect1);
});
Problem is...if you click on initial svg it will add rectangle to the mouse position. If you pan/zoom image and then add rectangle it will be shiffted.
It looks like problem is in method mySvg.getScreenCTM().inverse(). Matrix returned is always same one, panning and zooming does not change it. It always use matrix from initialy rendered svg. However, if I inspect svg element, I can see that pann/zoom change transform matrix directly on element (image below).
Does anybody know how to fix this. My requirement is to be able to drag and drop elements outside svg into svg on any zoom scale or pan context, so I need transformation from mouse click point to svg offset coordinates. If you know any other approach or any other library combination that could done this, it would be ok for me.
Thanks in advance.
Problem is, the transform isn't in mySvg. Its on the 'g' group element thats inside the svg. Zpd will create a group to operate on as far as I know, so you want to look at that.
To hightlight this, take a look at
console.log(mySvg.firstElementChild.getScreenCTM().inverse());
In this case its the g element (there's more direct ways of accessing it, depending on whether you want to just work in js, or snap, or svg.js).
jsfiddle
Its not quite clear from your description where you want the rect (within the svg, separate or whatt) to go and at what scale etc though, and if you want it to be part of the zoom/panning, or static or whatever. So I'm not sure whether you need this or not.
I'm guessing you want something like this
var tpt = pt.matrixTransform( mySvg.firstElementChild.getScreenCTM().inverse() )
var rect1 = snap.rect(tpt.x, tpt.y, 40, 40);
I have a project for manipulating SVGs.
Users can zoom in and out of the image. I want to have a thumbnail of the whole image that shows and highlights the area that users are currently zooming in/out.
Something along these lines http://www.ancientlives.org/transcribe
I have tried playing around with http://snapsvg.io/, without success.
Can anyone help working something out with the library?
As the specific question mentions Snap, I'll go down that road.
You could clone the svg element, and drag a rect over it, or I was wondering if you could drag a rect thats actually a clip or something, that could be a slightly better solution, but a bit fiddlier to work out, so for the moment here's the first way.
First off, we can load our image..
Snap.load("Dreaming_Tux.svg", onLoad)
Then the main onLoad func..
This works by cloning the image (I also use toDefs() which isn't necessary, but if the image is a large file, you could possibly use just one the set of elements, and reference them in a 'use' method. So I'm leaving that in as just a simple example for the moment.
We also define a viewBox,
var svg = s.svg(0,0,800,800,0,0,200,200);
Which will be our 'window'
And then when we drag the rect, we make the image (placed in a group so we can transform it) move.
You will need to tweak the drag handler, to make it work completely (atm it will just drag via dx,dy and reset each time) and also tweak the zoom and window sizes and relationship to what you want, but it should give a proof of concept.
example (drag the rect)
function onLoad( fragment ) {
s.append( fragment );
var tux = s.select('#tux');
var clone = tux.clone();
var svg = s.svg(0,0,800,800,0,0,200,200);
var g = s.g( tux ).transform('t0,0').appendTo(svg);
var defElement = svg.toDefs();
var dragRect = s.rect(0,0,100,100).attr({ opacity: 0.2, transform: 't600,50', id: 'dragrect' }).drag( dragMove, dragStart );
var tux1 = defElement.use().appendTo( s );
var tux2 = clone.appendTo( s.g().transform('t600,50s0.5') );
s.append( dragRect );
function dragMove(dx,dy) {
this.attr({
transform: this.data('origTransform') + (this.data('origTransform') ? "T" : "t") + [dx, dy]
});
g.transform('t' + -dx +',' + -dy);
}
function dragStart() {
this.data('origTransform', this.transform().local );
}
};
I'm trying to make a custom avatar maker, where my users can drag & drop images to the position they want (clothes etc. I take the image urls from database based on what they own). Then they can save the look as png image to my site (using php). I have no experience on javascript/jquery but I don't think that this can be without them. So I've found amazing code for this from here:
http://www.fabiobiondi.com/blog/2012/10/export-and-save-a-screenshot-of-an-html5-canvas-using-php-jquery-and-easeljs/
But the images are already in the canvas and can't go outside of it, which is bad considering that someone could have 100 pieces of clothing and didn't want to display them all. Also I have to make custom code for each piece, which is also bad because not all users have the same images to drag.
Is there a way to put all images draggable (to the canvas), so I could easily add the image urls from my database as basic html/css? Is it possible that the images would be outside of the canvas first? Or should I create another canvas for the items users don't want?
the script in the article uses a static image because its goal is only explain how to export a canvas to bitmap : )
I have written another small article where I describe how to upload N images from your hard drive into a canvas ( using CreateJS ) so as you can see the process to load dynamic sources is not so hard.
http://www.fabiobiondi.com/blog/2012/10/upload-images-from-the-user-hard-driveto-an-html5-canvas-easel-js-application/
Anyway, if you need to load an image into a canvas you can simply use a syntax like this:
var img = new createjs.Bitmap('http://uri/image.jpg')
img.x = 50;
img.y = 50;
stage.addChild(img)
stage.update();
and if you need to know when an image is completely loaded you should listen for the onload event:
var image = new Image();
image.onload = onImageLoaded;
image.src = "http://uri/image.jpg";
function onImageLoaded (event) {
var img = new createjs.Bitmap(event.target)
img.x = 50;
img.y = 50;
stage.addChild(img)
stage.update();
}
hope it's useful
I'm building a canvas user interface with jquery and fabric.js library and I set an overlay png image with a transparent section using the following code
var bgImgSrc = bgImg.attr('src');
canvas.setOverlayImage(bgImgSrc, function(img){
canvas.renderAll();
});
I also added an image behind the overlay and resized to fit the container using the following code
var photoImg = $('#img-photo');
var photoImgSrc = photoImg.attr('src');
fabric.Image.fromURL(photoImgSrc, function(img) {
var photoImgWidth = photoImg.width();
var photoImgHeight = photoImg.height();
var hRatio = 380/photoImgWidth;
var vRatio = 300/photoImgHeight;
var ratio = Math.min(hRatio, vRatio);
pImg = img.set({left: 380/2, top: 300/2, angle: 0})
img.scale(ratio).setCoords();
canvas.add(pImg);
canvas.sendToBack(pImg);
canvas.renderAll();
});
And it works as expected, however, when I click on the image to scale/resize it, I don't see the controls, except through the transparent space of the overlay image. Controls are behind the overlay image, is there a way to force show the image controls without having to put the entire image in front of the overlay?
Just set the boolean controlsAboveOverlay:
canvas.controlsAboveOverlay = true;
The problem here is that overlay image is rendered on top of objects' controls. This is expected behavior. Take a look at this wiki article, which shows the z-index of various things on fabric canvas and in which order they're rendered.
There are plans to add support for object controls that are always on top, as you can see from this ticket, but I can't tell you when that's going to happen.
You can also try using "after:render" callback and draw image manually onto canvas.
canvas.controlsAboveOverlay = true; //did the job... but:
I don't want to render controls above the overlay image (as overlay means overlay to me). I just want to render the stack exactly as described in Wiki (by default).
I just wonder...as described in the Wiki rendering-stack, the controls should be rendered on top of all objects (always visible by default), but they do not (look at kitchensink demo).
IMHO this behaviour should be set by a flag like canvas.controlsInStack = true.
By the way ... that thing is really beautiful!
I'm using Raphael.js to draw images to canvas. I need to be able to select certain image elements (this I can do) and make them look like they are selected (this is the problem).
Before Raphael.js I used regular Html5 canvas and simple rectangles. It was simple to delete selected rectangle and draw a new one with a different color to that same place.
But now that I'm using images, it's a different story. The image I'm using is here. It's a small gif.
So the question(s):
Is there a simple way to change color of a Raphael.js image-element programmatically?
Can I make an image-element to blink by changing its opacity?
Only requirement is that the selected element must be movable.
Code for drawing image when user clicks on canvas:
var NodeImage = RCanvas.image("../vci3/images/valaisin.gif", position.x, position.y, 30, 30);
NodeImage.toFront();
RSet.push(NodeImage);
NodeImage.node.id = 'lamp';
NodeImage.node.name = name;
NodeImage.click(function() {
console.log("Clicked on node " + NodeImage.node.name);
// Here should be the code that blinks or changes color or does something else
});
Is this completely bad idea? Is there a better way to achieve my goal?
i would suggest granting the image with an opacity of some level, and assign a value of 1 to it upon click:
NodeImage.attr('opacity', 0.6);
// ...
NodeImage.click(function() {
this.attr('opacity', 1);
});
of course, you will probably want to manage the shape's selected state, to switch the selected style off later on. in fact, you'll want to manage all selectable shapes in the same manner, so let's do that:
// keep all selectable shapes in a group to easily manage them
var selectableShapesArray = [NodeImage, otherNodeImage, anotherSelectableShape];
// define the behavior for shape click event
var clickHandler = function() {
for (var i in selectableShapesArray) {
var image = selectableShapesArray[i];
if (image.selected) {
image.attr('opacity', .6);
image.selected = false;
break;
}
}
this.attr('opacity', 1);
this.selected = true;
}
// attach this behavior as a click handler to each shape
for (var i in selectableShapesArray) {
var shape = selectableShapesArray[i];
shape.click(clickHandler);
}