Raphael, filling background with image (fillfit) - javascript

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>

Related

Snap.svg change attr on hover using this

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);

trying to build a basic basic game engine

I'm trying to teach myself about what goes into a game engine by building my own. when I said basic basic game engine, that was no typo. all I have so far is a simple text loader, and I'm working on a single image loader. I can not get my images to appear in the canvas even with and .onload = function style technique. In fact, I always have problems with images loading, I, just for shits and giggles, made a video loader that loaded four short videos, made and attached video tags and the divs that held them, and STILL, images drive me nut guys and girls! wtf! any help, but more importantly insight would be invaluable and helpful to a rookie at trying to understand as opposed to copying without understanding. Thanks in advance.
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
function drawText(context, pxSize, fontStyle, color, text, x, y){
context.font = pxSize + 'px ' + fontStyle;
context.fillStyle = color;
context.fillText(text, x, y);
}
function drawHero(src, sourceX, sourceY, sourceWidth, sourceHeight, x, y, height, width){
var hero = {
image: new Image(),
src: src,
sourceX: sourceX,
sourceY: sourceY,
sourceWidth: sourceWidth + 'px',
sourceHeight: sourceHeight + 'px',
x: x,
y: y,
width: width + 'px',
height: height + 'px'
};
hero.image.onload = function(){
context.drawImage(hero.image, hero.sourceX, hero.sourceY, hero.sourceWidth,
hero.sourceHeight, hero.x, hero.y, hero.width, hero.height);
}
hero.image.src = src;
}
drawHero("sonic.png" , "sonic1", 0, 0, 85, 119, 10, 10, 85, 119);
You defined hero.width and hero.height to be a string and that causes an error because context.drawImage needs a number for height and width.
Take away the width: width + "px" and height: height + "px" and replace it with width: width and height: height.
EDIT: just realized you did the same to hero.sourceWidth and hero.sourceHeight.
EDIT 2: just realized that you passed in 10 parameters to drawHero when it only takes in 9. You should remove "sonic1".
EDIT 3: You ordered the parameters to context.drawImage wrong too.
You might want to read this: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage .
Use this:
context.drawImage(hero.image, hero.x, hero.y, hero.width, hero.height, hero.sourceX, hero.sourceY, hero.sourceWidth, hero.sourceHeight);
Good luck with your game engine!
To have your images, and more generally your resources, loaded right, have a separate function to create the resources, and have a 'start' function that launches the engines once every resource is loaded.
var resourceCount = 0;
function loadResource(type, src) {
if (type == Image) {
resourceCount++;
var newImage = new Image();
newImage.onload = resourceLoaded;
// you might want to handle errors to ease debugging.
// newImage.onerror = ...
newImage.src = src;
return newImage;
}
// ... some other code for your text / sound / ... ressources
}
function resourceLoaded(loadedEvt) {
resourceCount--;
if (resourceCount == 0) startEngine();
}
// use with :
var heroImage = loadResource(Image, 'hero.png');

toSVG method not working on extended class with Fabric.js

I'm working on a project with Fabric.js.
Now I need to create a custom class and to export it to SVG.
I'm using Fabric.js tutorial to begin:
http://www.sitepoint.com/fabric-js-advanced/
Here is the javascript code:
var LabeledRect = fabric.util.createClass(fabric.Rect, {
type: 'labeledRect',
initialize: function(options) {
options || (options = { });
this.callSuper('initialize', options);
this.set('label', options.label || '');
},
toObject: function() {
return fabric.util.object.extend(this.callSuper('toObject'), {
label: this.get('label')
});
},
_render: function(ctx) {
this.callSuper('_render', ctx);
ctx.font = '20px Helvetica';
ctx.fillStyle = '#333';
ctx.fillText(this.label, -this.width/2, -this.height/2 + 20);
}
});
var canvas = new fabric.Canvas('container');
var labeledRect = new LabeledRect({
width: 100,
height: 50,
left: 100,
top: 100,
label: 'test',
fill: '#faa'
});
canvas.add(labeledRect);
document.getElementById('export-btn').onclick = function() {
canvas.deactivateAll().renderAll();
window.open('data:image/svg+xml;utf8,' + encodeURIComponent(canvas.toSVG()));
};
Here the HTML:
<canvas id="container" width="780" height="500"></canvas>
Export SVG
Here is my jsfiddle..
http://jsfiddle.net/QPDy5/
What I'm doing wrong?
Thanks in advance.
Every "class" in Fabric (rectangle, circle, image, path, etc.) knows how to output its SVG markup. The method responsible for it is toSVG. You can see toSVG of fabric.Rect, for example, or fabric.Text one.
Since you created subclass of fabric.Rect here and didn't specify toSVG, toSVG of the next object in prototype chain is used. The next "class" in the inheritance chain happens to be fabric.Rect, so you're seeing a result of fabric.Rect.prototype.toSVG.
The solution is simple: specify toSVG in your subclass. Theoretically, you would need to combine the code from fabric.Rect#toSVG and fabric.Text#toSVG, but to avoid repetition and keep things maintainable, we can utilize a bit of a hack:
toSVG: function() {
var label = new fabric.Text(this.label, {
originX: 'left',
originY: 'top',
left: this.left - this.width / 2,
top: this.top - this.height / 2,
fontSize: 20,
fill: '#333',
fontFamily: 'Helvetica'
});
return this.callSuper('toSVG') + label.toSVG();
}
The "fontSize", "fill", and "fontFamily" should ideally be moved to an instance-level property of course, to avoid repeating them there.
Here's a modified test — http://jsfiddle.net/fabricjs/QPDy5/1/
What #Sergiu Paraschiv suggested with a group is another just-as-viable solution.
Problem is there's no SVG equivalent to fillText so FabricJS seems to ignore it.
My workaround is to use a fabric.Group and build an inner Rect and Text
var LabeledRect = function(options) {
var rect = new fabric.Rect({
width: options.width,
height: options.height,
fill: options.fill
});
var label = new fabric.Text(options.label);
var group = new fabric.Group([rect, label]);
group.left = options.left;
group.top = options.top;
group.toSVG = function() {
var e=[];
for(var t = 0; t < this._objects.length; t++)
e.push(this._objects[t].toSVG());
return'<g transform="'+this.getSvgTransform()+'">'+e.join("")+"</g>"
};
return group;
};
The reason I overwrote toSVG is because the default implementation cycles through the children in reverse order (for(var t=this._objects.length;t--;)). I have no clue why it does that, but the text renders underneath the rectangle.

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