Changing image on click Raphael paper - raphael

I have a program where the user can add images to a paper, using Raphael. Now I want to be able to change an image to another image when clicked. Is this possible? Do I have to add an ID to the images? If so, how?
var imgURL = "img/img.png";
var bankNoteImg = new Image();
bankNoteImg.src = imgURL;
var width = bankNoteImg.width;
var height = bankNoteImg.height;
var image = paper.image(imgURL, X, Y, width, height);

Use the click and attr function of Raphael api.
Element.click will take a function as a parameter. Use this to access the image element inside the function.
I created a simple demo
var paper = Raphael(document.getElementById("papercanvas"), 200, 200);
var img = paper.image(URL1, 100, 100, 100 , 100);
function changeImageSource() {
this.attr('src', URL2);
}
img.click(changeImageSource);

Related

How to calculate Height of any control which is loaded in DataTemplate?

I want to get the height of the control which is loaded in DataTemplate when the width is set to 100. I have used the below code, but always returns the size 0,20. Any suggestion on this?
<DataTemplate x:Name="dataTemplate">
<Grid>
<TextBlock Text="{Binding Path=Name}" TextWrapping="Wrap"/>
</Grid>
</DataTemplate>
var record = new UserInfo() { Name = "ASKL ALASO DKADOLD ADKIKAM AMDKI ADKAI AKDKI" };
var contentControl = new ContentControl();
contentControl.Measure(new Size());
contentControl.Content = record;
contentControl.ContentTemplate = App.Current.Resources["dataTemplate"] as DataTemplate;
contentControl.Measure(new Size(100, Double.PositiveInfinity));
var size = contentControl.DesiredSize;
I have used the below code, but always returns the size 0,20. Any suggestion on this?
This is because you didn't render your ContentControl on the layout, you can render it for example like this:
var record = new UserInfo() { Name = "ASKL ALASO DKADOLD ADKIKAM AMDKI ADKAI AKDKI" };
var contentControl = new ContentControl();
stackPanel.Children.Add(contentControl); //Add this ContentControl to the childeren collection of a StackPanel to show it
//contentControl.Measure(new Size());
contentControl.Content = record;
contentControl.ContentTemplate = App.Current.Resources["dataTemplate"] as DataTemplate;
contentControl.Measure(new Size(100, Double.PositiveInfinity));
var size = contentControl.DesiredSize;
Then now, you can get the size of your ContentControl, by my side it is 94,100.
I'm not sure this size is the one you actually want, from your title, you want to calculate Height of any control which is loaded in DataTemplate, but this size is the size of ContentControl, I think what you need is the height of the Grid, or TextBlock inside of this ContentControl. To get this two control, you can code like this after the ContentControl is fully rendered:
var grid = contentControl.ContentTemplateRoot as Grid;
var textblock = grid.Children.FirstOrDefault() as TextBlock;
And be aware that the DesiredSize is not the rendered size of the control, you can refer to the Remarks part of UIElement.DesiredSize property.

Constrain RaphaelJS print to boundaries and position

I am looking for a more efficient way to constrain/set text in Raphael.
I have text that can be written in a box. That text should be centered (based on a number that could change if user wanted to shift text left/right) and the text cannot go beyond the boundaries of the paper.
This is what I do now and its not manageable performance wise
// Build a path
var path = this.paper.print(x, x, text, font, size, 'middle')
// Center line by getting bounding box and shifting to half of that
var bb = path.getBBox()
path.transform('...T' + [-bb.width / 2, 0])
// Compare paper size vs bb
// if it goes beyond I adjust X and Y accordingly and redo the above
So ideally I would like to predict the size of the text before it prints - I am not sure this is possible though as it is probably font dependent. I have looked for a command to contrain text but do not see one?
The other thought I had was to create some kind of shadow paper that does not print to screen and use that to determine size before I render to user. I am not sure where the lag is though - if it's in the screen rendering good but if its in the general logic of creating the svg then that wont help.
I'd appreciate suggestions
You can use opentype.js for measuring text, also you can get svg path by using Path.toPathData method. There is no need of cufon compiled js fonts.
For text measure, create a canvas element somewhere in your DOM.
<canvas id="canvas" style="display: none;"></canvas>
this function will load text and count width, height
function measureText(text, font_name, size) {
var dfd = $.Deferred();
opentype.load('/fonts/' + font_name + '.ttf', function (err, font) {
if (err) {
dfd.reject('Font is not loaded');
console.log('Could not load font: ' + err);
} else {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
var tmp_path = font.getPath(text, 100, 100, size);
tmp_path.draw(ctx); // Draw path on the canvas
var ascent = 0;
var descent = 0;
var width = 0;
var scale = 1 / font.unitsPerEm * size;
var glyphs = font.stringToGlyphs(text_string);
for (var i = 0; i < glyphs.length; i++) {
var glyph = glyphs[i];
if (glyph.advanceWidth) {
width += glyph.advanceWidth * scale;
}
if (i < glyphs.length - 1) {
var kerningValue = font.getKerningValue(glyph, glyphs[i + 1]);
width += kerningValue * scale;
}
ascent = Math.max(ascent, glyph.yMax);
descent = Math.min(descent, glyph.yMin);
}
return dfd.resolve({
width: width,
height: ascent * scale,
actualBoundingBoxDescent: descent * scale,
fontBoundingBoxAscent: font.ascender * scale,
fontBoundingBoxDescent: font.descender * scale
});
}
});
return dfd.promise();
},
then use this function to get text dimensions:
$.when(measureText("ABCD", "arial", 48)).done(function(res) {
var height = res.height,
width = res.width;
...
});

Path animation for image

I have to do some path based animation But the object which has to move over the path is IMAGE and NOT any rect/circle ...etc.
I am new to raphael.js and found no good documentation/demo regarding path animation for any external image.
For E.g.
var paper = Raphael(document.getElementById('pond'), 500, 256);
var fairyPath = paper.path('M70,75 q-85,50 100,0 q200,-140 -100,0');
var fairy = paper.image("assets/fairy.png", 0, 0, 30, 30);
fairy.animate({path: fairyPath}, 3000);
How can i related fairy with fairyPath and create animation?

How to access Raphael paper across multiple methods in Meteor

I'm using Raphael with Meteor and have run into a problem. I'm creating a paper by using var paper = Raphael("paper", 800, 600); If I put this code in a rendered function, like Template.board.rendered, I can use it just fine. However, the paper var from this template apparently can't be accessed by other functions, even if I call them from within the template. I thought that I could get around this by creating the paper in Meteor.startup, but I still have the same problem.
For instance, the following code doesn't work:
Pieces = new Meteor.Collection('pieces');
Meteor.startup(function () {
var paper = Raphael("paper", 800, 600);
});
Template.board.rendered = function () {
// If I create the paper in here along with the contents of createGoban
// it works, but then I can't use it anywhere else...
// var paper = Raphael("paper", 800, 600);
createGoban();
};
var createGoban = function () {
// Create goban
// Based off of svg in public domain: http://commons.wikimedia.org/wiki/File:Blank_Go_board.svg
// Converted using http://readysetraphael.com/
var rect_a = paper.rect(0, 0, 96, 96);
rect_a.attr({fill: '#DCB35C','stroke-width': '0','stroke-opacity': '1'}).data('id', 'rect_a');
var path_b = paper.path("M2.9,93h90.2m-.2-5H3m0-5h90m0-5H3m0-5h90m0-5H3m0-5h90m0-5H3m0-5h90m0-5H3m0-5h90m0-5H3m0-5h90m0-5H3m0-5h90m0-5H3m0-5h90m0-5H3m-.1-5h90.2M3,3V93m5,0V3m5,0V93m5,0V3m5,0V93m5,0V3m5,0V93m5,0V3m5,0V93m5,0V3m5,0V93m5,0V3m5,0V93m5,0V3m5,0V93m5,0V3m5,0V93m5,0V3m5,0V93m5,0V3");
path_b.attr({stroke: '#000',"stroke-width": '0.2',fill: 'none','stroke-opacity': '1'}).data('id', 'path_b');
var path_c = paper.path("M18,78l0,0m30,0l0,0m30,0l0,0m0-30l0,0m-30,0l0,0m-30,0l0,0m0-30l0,0m30,0l0,0m30,0l0,0");
path_c.attr({stroke: '#000',"stroke-width": '4',"stroke-linecap": 'round','stroke-opacity': '1','fill': '#000000'}).data('id', 'path_c');
var goban = [rect_a, path_b, path_c];
// Scale goban up since rsr outputs reduced size
for(var i = 0; i < goban.length; i++) {
goban[i].transform('S5,5,0,0');
};
};
Your code works fine after removing the var inside startup(), which makes paper global:
Meteor.startup(function () {
paper = Raphael("paper", 800, 600);
});

Making charts in RaphaelJS that are 100% width?

I have seen graphs in Flash & stuff that basically adapt nicely to whatever the size of the browser or flexible element they are inside of.... I'm not really too well versed with raphaelJS but can you do this, and if so, how?
In raphaeljs, you can call .setSize on a Raphael object to update its size. To adapt to runtime changes in browser window size, you can respond to a window resize event. Using jQuery, you could do:
// initialize Raphael object
var w = $(window).width(),
h = $(window).height();
var paper = Raphael($("#my_element").get(0), w,h);
$(window).resize(function(){
w = $(window).width();
h = $(window).height();
paper.setSize(w,h);
redraw_element(); // code to handle re-drawing, if necessary
});
This will get you a responsive SVG
var w = 500, h=500;
var paper = Raphael(w,h);
paper.setViewBox(0,0,w,h,true);
paper.setSize('100%', '100%');
Normally you could set the width to %100 and define a viewBox within SVG. But, Raphael JS manually set a width and height directly on your SVG elements, which kills this technique.
Bosh's answer is great, but it was distorting the aspect ratio for me. Had to tweak a few things to get it working correctly. Also, included some logic to maintain a maximum size.
// Variables
var width;
var height;
var maxWidth = 940;
var maxHeight = 600;
var widthPer;
function setSize() {
// Setup width
width = window.innerWidth;
if (width > maxWidth) width = maxWidth;
// Setup height
widthPer = width / maxWidth;
height = widthPer * maxHeight;
}
var paper = Raphael(document.getElementById("infographic"), 940, 600);
paper.setViewBox(0, 0, 940, 600, true);
window.onresize = function(event) {
setSize();
paper.setSize(width,height);
redraw_element();
}