raphael.js how to add id to a path - javascript

I just started using Raphael.js and I am stumped on adding an id to a path. I have read a lot of posts on how to do it, but I think the way my file is set up doesn't allow me to easily translate those solutions.
I have an init.js file and a path.js file
init.js
var r = Raphael('man', 500, 573),
attributes = {
fill: '#204ad3',
opacity: '0.0',
'stroke-linejoin': 'round'
},
arr = new Array();
for (var muscles in paths) {
var obj = r.path(paths[muscles].path);
obj.attr(attributes);
arr[obj.id] = muscles;
obj
.hover(function(){
this.animate({
fill: '#204ad3',
opacity: '0.3'
}, 300);
}, function(){
this.animate({
fill: attributes.fill,
opacity: attributes.opacity
}, 300);
})
.click(function(){
document.location.hash = arr[this.id];
var point = this.getBBox(0);
$('#man').next('.point').remove();
$('#man').after($('<div />').addClass('point'));
$('.point')
.html(paths[arr[this.id]].name)
.prepend($('<a />').attr('href', '#').addClass('close').text('Close'))
.prepend($('<img />').attr('src', 'flags/'+arr[this.id]+'.png'))
.css({
left: point.x+(point.width/2)-80,
top: point.y+(point.height/2)-20
})
.fadeIn();
});
$('.point').find('.close').live('click', function(){
var t = $(this),
parent = t.parent('.point');
parent.fadeOut(function(){
parent.remove();
});
return false;
});
}
path.js file:
var paths = {
neck: {
name: 'Neck',
path: 'd="M412.294,73.035c0,0,7.661,28.869,9.406,31.78c1.746,2.911,4.657,2.911,9.896,2.911 s9.313-1.746,9.896-5.239c0.582-3.493,6.985-28.523,6.985-28.523s-2.963,2.599-6.232,5.984c-2.543,2.632-7.2,5.904-11.088,5.904 c-3.889,0-6.705-2.431-10.367-5.04C418.063,78.868,418.08,79.22,412.294,73.035z"',
},
pecks: {
name: 'Pecks',
path: 'd="M379.581,117.994c0,0,0.396-1.586,6.936-4.558c6.539-2.972,13.475-5.351,16.844-6.737 c3.369-1.387,4.559-1.784,4.559-1.784s13.674,2.973,15.061,3.17c1.387,0.198,4.36,1.982,8.72,1.982s9.511-1.387,11.097-2.18 s10.307-2.973,11.693-3.171s1.387-0.198,1.387-0.198s12.286,3.369,16.845,4.36c4.558,0.991,8.917,2.378,9.116,3.765 c0.197,1.387,1.584,4.954,1.584,6.341s-0.197,5.945-0.396,6.738c-0.198,0.792-3.171,15.457-4.757,21.997 c-1.585,6.54-1.188,8.918-7.331,11.494s-10.899,7.53-22.79,2.378s-13.277-5.549-17.241-5.152s-11.098,3.963-14.862,5.351 c-3.766,1.387-16.251,2.179-20.412-0.198c-4.162-2.378-10.9-9.314-12.881-16.844c-1.981-7.531-3.963-16.052-4.359-17.638 C377.995,125.525,377.798,121.165,379.581,117.994z"',
},
}
The generated SVG element does not have an id, nor do I know how to get one in there. Any advise/help would be much appreciated.

This will add the id attribute to the svg path elements:
var id = 0;
for (var muscles in paths) {
var obj = r.path(paths[muscles].path);
obj.node.id = "muscles_or_whatever_you_want_" + id;
id++
//....

to add an id to a SVG element ....
paper.path( path data ).node.id = 'pathIdString';. to test ... try alert( document.getElementById("pathIdString").id );. should return the id

It looks like what you are trying to do is to pop up a div with an image and a close link above the path that is clicked. The problem is that you are confusing the Raphael id of the path object with the index in your paths array. You probably want to eliminate arr entirely and pass the index to your handler through a closure:
.click(function(index) { return function() {
document.location.hash = index;
var point = this.getBBox(0);
$('#man').next('.point').remove();
$('#man').after($('<div />').addClass('point'));
$('.point')
.html(paths[index].name)
.prepend($('<a />').attr('href', '#').addClass('close').text('Close'))
.prepend($('<img />').attr('src', 'flags/'+index+'.png'))
.css({
left: point.x+(point.width/2)-80,
top: point.y+(point.height/2)-20
})
.fadeIn();
}; }(muscles));

Related

show each div content individually in each page

I am exporting the data present inside the div to PDF when user click on the export button. I want to show each div content to show in individual pages inside the PDF.
The above scenario is working in the demo https://plnkr.co/edit/KvkVlYmmmJiZ71sghb1l?p=preview
The same when applied to below code, it is not working.
Demo here : https://plnkr.co/edit/P9nUSRY5TytkonM6dUHl?p=preview
js code:
$scope.export = function() {
var pdf = new jsPDF('landscape');
var source = $('#append-source');
$('.myDivClass').each(function(){
var html = "<div>"+$(this) + "</div><!--ADD_PAGE-->";//the code is broken with this line
// var html = $(this);
source.append(html);
});
console.log(source);
pdf.addHTML(
source, 0, 0, {
pagesplit: true
},
function(dispose){
pdf.save('test3.pdf');
}
);
}
It is not recommended to use jquery like this inside an angular app. To see why look here: Can we use both jQuery and Angular in our Web Application?
However what you want to do is possible if you put the following into your controller:
$scope.export = function() {
var pdf = new jsPDF('landscape');
var source = "";
var width1 = pdf.internal.pageSize.width;
$('.myDivClass').each(function(){
var textForPdfPage = $(this).children().eq(1).children()[0].textContent;
var html = "<div>"+ textForPdfPage + " </div><!--ADD_PAGE-->";
source+=html;
});
margins = {
top: 80,
bottom: 60,
left: 10,
width: '100%'
};
pdf.fromHTML(
source, // HTML string or DOM elem ref.
margins.left, // x coord
margins.top, { // y coord
'width': width1 // max width of content on PDF
},
function (dispose) {
pdf.save('test.pdf');
},
margins
);
}
Your main problem is that when you where trying to create your html string you only used $(this). $(this) gives you a jquery object. The string you want to put on the page is inside this object and is accessed using the jquery .children() method.
Here is a way of doing what you asked using addHTML() instead of fromHTML():
$scope.export = function() {
var pdf = new jsPDF('landscape');
var pdfName = 'test.pdf';
var options = {};
var $divs = $('.myDivClass') //jQuery object of all the myDivClass divs
var numRecursionsNeeded = $divs.length -1; //the number of times we need to call addHtml (once per div)
var currentRecursion=0;
//Found a trick for using addHtml more than once per pdf. Call addHtml in the callback function of addHtml recursively.
function recursiveAddHtmlAndSave(currentRecursion, totalRecursions){
//Once we have done all the divs save the pdf
if(currentRecursion==totalRecursions){
pdf.save(pdfName);
}else{
currentRecursion++;
pdf.addPage();
//$('.myDivClass')[currentRecursion] selects one of the divs out of the jquery collection as a html element
//addHtml requires an html element. Not a string like fromHtml.
pdf.addHTML($('.myDivClass')[currentRecursion], 15, 20, options, function(){
console.log(currentRecursion);
recursiveAddHtmlAndSave(currentRecursion, totalRecursions)
});
}
}
pdf.addHTML($('.myDivClass')[currentRecursion], 15, 20, options, function(){
recursiveAddHtmlAndSave(currentRecursion, numRecursionsNeeded);
});
}
I left the other answer so people can see both ways of doing it.

Fabric.js - canvas.toDatalessJSON not working as expected

This is how i load images on to the canvas:
$(".img-link").on("click", function(e) {
e.preventDefault();
var url = $(this).attr('href'),
extension = url.substr((~-url.lastIndexOf(".") >>> 0) + 2);
if(extension !== 'svg') {
fabric.Image.fromURL( url, function(oImg) {
var ow = oImg.getWidth(),
oh = oImg.getHeight();
oImg.lockUniScaling = true;
oImg.set({'left': ow/2, 'top': oh/2});
canvas.add(oImg);
});
} else {
var group = [];
fabric.loadSVGFromURL(url, function(objects,options) {
var loadedObjects = new fabric.Group(group);
loadedObjects.set({
left: 100,
top: 100,
sourcePath: url
});
canvas.add(loadedObjects);
canvas.renderAll();
},function(item, object) {
object.set('id',item.getAttribute('id'));
group.push(object);
});
}
});
console.log(JSON.stringify(canvas.toDatalessJSON())) gives the following output:
{"objects":[{"type":"group","originX":"center","originY":"center","left":100,"top":100,"width":200,"height":200,"fill":"rgb(0,0,0)","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"objects":[{"type":"path","originX":"center","originY":"center","left":0,"top":0,"width":200,"height":200,"fill":"#aa0000","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"path":[["M",91.5,73],["c",0,-30,40,-30,40,0],["c",0,30,-40,60,-40,60],["c",0,0,-40,-30,-40,-60],["c",0,-30,40,-30,40,0]],"pathOffset":{"x":0,"y":0}}]}],"background":""}
if i check canvas.item(0).sourcePath it gives me heart.svg
Whats wrong?
Thanks
You are using fabric.Group instead of fabric.Path or fabric.PathGroup. Only fabric.PathGroup and fabric.Path replaces path definition with sourcePath url if you are calling canvas.toDatalessJSON().
Use that code for your svg elements:
fabric.loadSVGFromURL(url, function(objects, options) {
// Group elements to fabric.PathGroup (more than 1 elements) or
// to fabric.Path
var loadedObject = fabric.util.groupSVGElements(objects, options);
// Set sourcePath
loadedObject.set('sourcePath', url);
canvas.add(loadedObject);
});
Here you can see a jsfiddle: http://jsfiddle.net/Kienz/526wC/
In your jsfiddle you are using fabric.Image and not fabric.Path.
It looks like you set sourcePath attribute on fabric.Group object instead of fabric.Path.
If you set the sourcePath attribute to your path object it should work.

Fade in image only after finish loading

I've some images on a page which are loaded randomly and they are over 100kbs, is it possible to have it fully loaded then fade it in rather than progressively loading it?
My JS looks like this...
(function($){
$.randomImage = {
defaults: {
//you can change these defaults to your own preferences.
path: '_images/', //change this to the path of your images
myImages: ['hero_eagle.jpg', 'hero_giraffe.jpg', 'hero_owl.jpg', 'hero_rabbit.jpg']
}
}
$.fn.extend({
randomImage:function(config) {
var config = $.extend({}, $.randomImage.defaults, config);
return this.each(function() {
var imageNames = config.myImages;
//get size of array, randomize a number from this
// use this number as the array index
var imageNamesSize = imageNames.length;
var lotteryNumber = Math.floor(Math.random()*imageNamesSize);
var winnerImage = imageNames[lotteryNumber];
var fullPath = config.path + winnerImage;
//put this image into DOM at class of randomImage
// alt tag will be image filename.
$(this).attr( { src: fullPath });
});
}
});
})(jQuery);
Should be able to, just set the image to display:none in your stylesheet and modify the bit of the script that sets the src to this:
$(this).attr( { src: fullPath }).load(function() {
$(this).fadeIn()
});
Start with the images hidden using CSS. Then use:
$(document).ready(function() {
// Code goes here.
});
and have the fade-in execute there.
There's another SO question that discusses preloading images using jQuery right here: Preloading images with jQuery
Quoting from the top answer:
function preload(arrayOfImages) {
$(arrayOfImages).each(function(){
$('<img/>')[0].src = this;
// Alternatively you could use:
// (new Image()).src = this;
});
}
// Usage:
preload([
'img/imageName.jpg',
'img/anotherOne.jpg',
'img/blahblahblah.jpg'
]);
if you want all images to preload before fading in, and display a loading message to the user, you can use something like this:
var gotime = imgArray.length;
$(".maxCount").text(gotime);
var imgCounter = 0;
$.each(imgArray, function(){
$(new Image()).load(function(){
imgCounter++;
$(".presentCount").text(imgCounter);
if (--gotime < 1) {
$("#content").fadeIn();
}
}).attr('src', this);
});

JQuery custom function even

I am writing my first custom function/addon for jQuery, it's been a lot of fun. basically it will load a bunch of images from an array and create a slider to enable you to pick one from the list and display it as the main image.
in my options I have added an onClick event, but I want to be able to pass variables to this event for the end user to set in the options, such as the image src, so when you click an image the optional onClick function fires and the end user could then return the src of the image clicked, he is my code for a better example, it may be a tad dirty as I haven't cleaned it and its full of code that needs polishing but it works.
(function($){
$.fn.imagePicker = function( options ) {
var defaults = {
width: 398,
height: 230,
imageArray: ["/img/news/emailpi.jpg", "/img/news/debit.jpg", "/img/news/aim-high.jpg", "/img/news/Koala.jpg", "/img/news/170217_get_connected.jpg",
"/img/news/10.jpg", "/img/news/1000864_street_variations__doors_2.jpg", "/img/news/1005134_ear_defenders.jpg", "/img/news/1080177_magnifying_glass.jpg", "/img/news/160.jpg",
"/img/news/161.jpg", "/img/news/162.jpg", "/img/news/163.jpg", "/img/news/2012.jpg", "/img/news/48.jpg",
"/img/news/8.jpg", "/img/news/12.jpg", "/img/news/ski.jpg", "/img/news/storm.jpg", "/img/news/tax-img.jpg",],
show: 10,
thumb: {
width:70,
height: 60,
borderWidth: 1,
borderColour: "#CCC",
borderStyle: "solid",
padding:3
},
mainImage: {
width: 150,
height:200,
borderColour:"#CCC",
borderStyle:"solid",
padding:3
},
leftScroll: 50,
rightScroll: 50,
interval: 5,
onClick: function(){},
onScrollLeft:function(){},
onScrollRight:function(){},
debug: false
};
var options = $.extend( true, defaults, options );
//Preload the images in the array
ip_preloader( options.imageArray )
$(this).css({width:options.width, height:options.height, overflow:'hidden'});
$(this).html( "<div class='ip-main-images' style='text-align:center'></div>\n\
<div class='ip-image-menu' style='position:relative; height:"+(options.thumb.height+5)+"px; width:"+( ((options.thumb.width + options.thumb.padding + ( options.thumb.borderWidth*2 ))*options.imageArray.length) )+"px; overflow:hidden;'>\n\
<div class='ip-menu-images-overlay' style='width:"+options.width+"px; position:absolute; top:0;'>\n\
<div class='ip-left-hotspot' style='background:url(/admin_v2/css/images/white_trans_10x10.png);position:absolute; left:0; top:0; width:"+options.leftScroll+"px; height:"+( options.thumb.height+( options.thumb.borderWidth*2 ) )+"px; z-index:99;'></div>\n\
<div class='ip-right-hotspot' style='background:url(/admin_v2/css/images/white_trans_10x10.png);position:absolute; right:0; top:0; width:"+options.rightScroll+"px; height:"+( options.thumb.height+( options.thumb.borderWidth*2 ) )+"px; z-index:99;'></div>\n\
</div>\n\
<div class='ip-menu-images' style='position:relative; top:0;'>\n\
</div>\n\
</div>\n\
<div class='ip-mouse-pos'></div>" );
var container = $(this);
var menu = $(this).find( ".ip-menu-images" );
var main = $(this).find( ".ip-main-images" );
main.html( "<img src='"+options.imageArray[0]+"' width='"+options.mainImage.height+"' height='"+options.mainImage.width+"' style='margin:0 auto; width:"+options.mainImage.width+"px' />" );
for( i=0; i<options.imageArray.length; i++ ){
menu.append( "<img src='"+options.imageArray[i]+"' style='float:left; margin-right:"+options.thumb.padding+"px; border:"+options.thumb.borderWidth+"px "+options.thumb.borderStyle+" "+options.thumb.borderColour+";' alt='' class='' width='"+options.thumb.width+"' height='"+options.thumb.height+"' />" );
}
$(".ip-image-menu img").live("click", function( ){
var src = $(this).attr("src");
$(".ip-main-images img").attr("src", src);
options.onClick( src );
});
var leftpos = 0;
$(".ip-left-hotspot").bind('mouseenter', function() {
this.iid = setInterval(function() {
if( leftpos != options.leftScroll ){
leftpos ++;
menu.css("left", leftpos);
options.onScrollLeft();
}
if(options.debug)$(".ip-mouse-pos").html( "LeftPos: "+menu.css("left") );
}, options.interval);
}).bind('mouseleave', function(){
this.iid && clearInterval(this.iid);
});
$(".ip-right-hotspot").bind('mouseenter', function() {
this.iids = setInterval(function() {
if( leftpos != "-"+menu.width()){
leftpos --;
menu.css("left", leftpos);
options.onScrollRight();
}
if(options.debug)$(".ip-mouse-pos").html( "LeftPos: "+menu.css("left") );
}, options.interval);
}).bind('mouseleave', function(){
this.iids && clearInterval(this.iids);
});
function ip_preloader( imagesArray )
{
// counter
var i = 0;
// create object
imageObj = new Image();
// start preloading
for(i=0; i<=3; i++)
{
imageObj.src=imagesArray[i];
}
}
; };
})(jQuery);
if you do a find for ("src") will jump you to the bit of code where I call options.onClick and I just want to pass the src to that event so the end user has access to it, should they want to populate a hidden form field with the image src etc. And also so I know how it works, as I am struggling to google for it without the correct terminology. (am not even sure if what I am trying to do is called "an event")
You could use the following:
if(typeof(options.onClick) == 'function'){
options.onClick(x, y, z);
}
You can just give the parameters you want to the function. JavaScript doesn't care how many arguments you give the function. You subscriber just neest to specify the parameters in his handler.
This example shows that it works.
var onclick;
onclick = function(a){
alert(a);
};
onclick('Hellow world', 'I am useless');
So this should work as well:
$('#myElement').imagePicker({
onclick: function(src){
alert(src);
}
});
Maybe you could make your example a bit smaller. This bulk of code is a little bit to much.
This is a loose example but you could allow the user to pass function into the constructor, in this example I am passing an object of callbacks but afaik it would amount to the same thing
var a = "xyz";
var Foo = function(args, callbacks) {
var a = "abc";
this.bar = callbacks.bar(a);
};
var foo = new Foo({}, { bar: function(a){ alert(a) } });
foo.bar(); // will alert "abc" NOT "xyz"
here is a demo

Ext.draw: how to control sprites?

I'm trying to make a drawing application with ExtJS4, the MVC-way.
For this, I want to make several widgets. This is my code for a widget:
VIEW:
Ext.define('VP.view.fields.Field' ,{
extend: 'Ext.draw.Sprite',
alias: 'widget.field',
constructor: function() {
var parentConfig = {
id: 'field',
fill: '#FFFF00',
type: 'rect',
width: '100%',
height: '100%',
x: 0,
y: 0
};
this.callParent([parentConfig]);
}
});
CONTROLLER:
Ext.define('VP.controller.Fields', {
extend: 'Ext.app.Controller',
views: [
'fields.Field'
],
init: function() {
console.log('Initialized Field controller!');
var me = this;
me.control({
'field': { //trying to control the widget.field, this fails
//'viewport > draw[surface]': { //this controls the surface successfully
//'viewport > draw[surface] > field': { //fails
click: this.addObject
}
});
},
addObject : function(event,el){
console.log(el);
//console.log(drawComponent);
}
});
How can I control this custom sprite? Can only Ext.components be controlled by a controller? (Ext.draw.Sprite doesn't extend Ext.component).
In addition to my previous answer, you could add the listener on the sprite and fire an event on some object. Then you could catch it in the controller.
Example:
spriteObj....{
listeners: {
click: {
var canvas = Ext.ComponentQuery.query('draw[id=something]')[0];
var sprite = this;
canvas.fireEvent('spriteclick',sprite);
}
}
}
In the controller:
init: function() {
console.log('Initialized Field controller!');
var me = this;
me.control({
'draw[id=something]': {
spriteclick: this.doSomething
}
});
},
doSomething: function (sprite) {
console.log(sprite);
}
I've been messing around with the same thing. It appears that you can't do it because it is an svg object that Ext just 'catches' and adds a few events to it. Because it doesn't have an actual alias (xtype) because it is not a component, it cannot be queried either. You have to do the listeners config on the sprite unfortunately.
Also, I tried doing the method like you have of extending the sprite. I couldn't get that to work, I assume because it is not a component.
I was able to keep this in the all in the controller because I draw the sprites in the controller. So, I can manually define the listeners in there and still use all the controller refs and such. Here's an example of one of my drawing functions:
drawPath: function(canvas,points,color,opacity,stroke,strokeWidth, listeners) {
// given an array of [x,y] coords relative to canvas axis, draws a path.
// only supports straight lines
if (typeof listeners == 'undefined' || listeners == "") {
listeners = null;
}
if (stroke == '' || strokeWidth == '' || typeof stroke == 'undefined' || typeof strokeWidth == 'undefined') {
stroke = null;
strokeWidth = null;
}
var path = path+"M"+points[0][0]+" "+points[0][1]+" "; // start svg path parameters with given array
for (i=1;i<points.length;i++) {
path = path+"L"+points[i][0]+" "+points[i][1]+" ";
}
path = path + "Z"; // end svg path params
var sprite = canvas.surface.add({
type: 'path',
opacity: opacity,
fill:color,
path: path,
stroke:stroke,
'stroke-width': strokeWidth,
listeners: listeners
}).show(true);
this.currFill = sprite;
}
You can see where I just pass in the parameter for the listeners and I can define the object elsewhere. You could do this in your own function or wherever in the controller. While the actual sprite should be in the view, this keeps them a little more dynamic and allows you to manipulate them a little easier.

Categories

Resources