Related
See Fiddle: I am trying to render a background image on a FabricJS polygon, but for some reason the scale of the pattern is different on different devices / browsers for no obvious reason.
Here is the code:
HTML:
<canvas id="tcanvas" width="200" height="200" />
JavaScript:
function testPattern(id) {
var url = 'https://dummyimage.com/200x200/aff/000',
tCanvas = new fabric.Canvas(id);
new fabric.Image.fromURL(url, function(img) {
var tPoints = [{
x: 0,
y: 0
},
{
x: 0,
y: 200
},
{
x: 200,
y: 200
},
{
x: 200,
y: 0
}
];
var tPatternWidth = 200;
var tPatternHeight = 200;
var tImgXScale = tPatternWidth / img.width;
var tImgYScale = tPatternHeight / img.height;
img.set({
left: 0,
top: 0,
scaleX: tImgXScale,
scaleY: tImgYScale
});
var tPatternSourceCanvas = new fabric.StaticCanvas();
tPatternSourceCanvas.add(img);
tPatternSourceCanvas.renderAll();
var tPattern = new fabric.Pattern({
offsetX: 0,
offsetY: 0,
source: function() {
tPatternSourceCanvas.setDimensions({
width: tPatternWidth,
height: tPatternHeight
});
tPatternSourceCanvas.renderAll();
return tPatternSourceCanvas.getElement();
},
repeat: 'no-repeat'
});
var tImgPoly = new fabric.Polygon(tPoints, {
left: 100,
top: 100,
originX: 'center',
originY: 'center',
fill: tPattern,
objectCaching: false,
selectable: false,
stroke: 'red',
strokeWidth: 1
});
tCanvas.add(tImgPoly);
tCanvas.renderAll();
});
}
testPattern('tcanvas');
This is what I see on my desktop Chrome:
... and this what I see on a (Samsung) tablet Chrome:
How can I fix the code so that the both patterns look the same, i.e., like the first one?
It s a retina support side effect.
When initializing the canvas for the Pattern, please pass as option:
enableRetinaScaling: false
var tPatternSourceCanvas = new fabric.StaticCanvas(null, {enableRetinaScaling: false});
This will avoid fabric trying to enhance the canvas twice.
I use snap.svg, and try to achieve such bechavior. The svg should be scale and centered on mobile, like on the image. Also ist possible when hovering over dots - country names should appear & be clickable links. In any case is possible to optimize the code? And how to centered svg on mobile one? Codepen update
var s = Snap("#svg");
var startPoint, endPoint;
var path;
var circleProps = {
fill: 'white',
strokeWidth: 3,
opacity: 0,
class: "cicles"
};
circleProps.stroke = 'white';
startPoint = s.circle(770,415,5);
startPoint.attr(circleProps);
endPoint = s.circle(795,386,5);
endPoint.attr(circleProps);
endPoint1 = s.circle(820,445,5);
endPoint1.attr(circleProps);
endPoint2 = s.circle(900,500,5);
endPoint2.attr(circleProps);
endPoint3 = s.circle(1050,630,5);
endPoint3.attr(circleProps);
endPoint4 = s.circle(1170,540,5);
endPoint4.attr(circleProps);
endPoint5 = s.circle(810,600,5);
endPoint5.attr(circleProps);
endPoint6 = s.circle(750,460,5);
endPoint6.attr(circleProps);
text = s.text(735,350, ["LATVIA"]);
text.attr({
fill: "white",
"font-size": "20px",
"font-family": "Verdana",
"background": "black",
});
p1 = text.selectAll("tspan:nth-child(2)");
p3 = text.selectAll("tspan:nth-child(4)");
path = s.path("M770,415,Q790,360,795,386").attr({
fill: "none",
stroke: "white",
strokeWidth: 4
});
path1 = s.path("M770,415,Q800,410,820,445").attr({
fill: "none",
stroke: "white",
strokeWidth: 4
});
path2 = s.path("M770,415,Q880,410,900,500").attr({
fill: "none",
stroke: "white",
strokeWidth: 4
});
path3 = s.path("M770,415,Q1060,410,1050,630").attr({
fill: "none",
stroke: "white",
strokeWidth: 4
});
path4 = s.path("M770,415,Q1060,410,1170,540").attr({
fill: "none",
stroke: "white",
strokeWidth: 4
});
path5 = s.path("M770,415,Q810,410,810,600").attr({
fill: "none",
stroke: "white",
strokeWidth: 4
});
path6 = s.path("M770,415,Q810,410,750,460").attr({
fill: "none",
stroke: "white",
strokeWidth: 4
});
var pathLength = Snap.path.getTotalLength( path );
path.node.style.strokeDasharray = pathLength + ' ' + pathLength;
path.node.style.strokeDashoffset = pathLength;
var pathLength1 = Snap.path.getTotalLength( path1 );
path1.node.style.strokeDasharray = pathLength1 + ' ' + pathLength1;
path1.node.style.strokeDashoffset = pathLength1;
var pathLength2 = Snap.path.getTotalLength( path2 );
path2.node.style.strokeDasharray = pathLength2 + ' ' + pathLength2;
path2.node.style.strokeDashoffset = pathLength2;
var pathLength3 = Snap.path.getTotalLength( path3 );
path3.node.style.strokeDasharray = pathLength3 + ' ' + pathLength3;
path3.node.style.strokeDashoffset = pathLength3;
var pathLength4 = Snap.path.getTotalLength( path4 );
path4.node.style.strokeDasharray = pathLength4 + ' ' + pathLength4;
path4.node.style.strokeDashoffset = pathLength4;
var pathLength5 = Snap.path.getTotalLength( path5 );
path5.node.style.strokeDasharray = pathLength5 + ' ' + pathLength5;
path5.node.style.strokeDashoffset = pathLength5;
var pathLength6 = Snap.path.getTotalLength( path6 );
path6.node.style.strokeDasharray = pathLength6 + ' ' + pathLength6;
path6.node.style.strokeDashoffset = pathLength6;
p1.animate({
fill: "white",
}, 600, mina.easeout);
startPoint.animate({
r: 5,
opacity: 1
}, 600, mina.easeout);
setTimeout(function() {
endPoint.animate({
r: 5,
opacity: 1
}, 600, mina.easeout);
endPoint1.animate({
r: 5,
opacity: 1
}, 600, mina.easeout);
endPoint2.animate({
r: 5,
opacity: 1
}, 600, mina.easeout);
endPoint3.animate({
r: 5,
opacity: 1
}, 600, mina.easeout);
endPoint4.animate({
r: 5,
opacity: 1
}, 600, mina.easeout);
endPoint5.animate({
r: 5,
opacity: 1
}, 600, mina.easeout);
endPoint6.animate({
r: 5,
opacity: 1
}, 600, mina.easeout);
}, 1800);
setTimeout(function() {
Snap.animate(pathLength, 0, function( value ) {
path.node.style.strokeDashoffset = value;
}, 1600);
}, 2700);
setTimeout(function() {
Snap.animate(pathLength1, 0, function( value ) {
path1.node.style.strokeDashoffset = value;
}, 1600);
}, 2700);
setTimeout(function() {
Snap.animate(pathLength2, 0, function( value ) {
path2.node.style.strokeDashoffset = value;
}, 1600);
}, 2700);
setTimeout(function() {
Snap.animate(pathLength3, 0, function( value ) {
path3.node.style.strokeDashoffset = value;
}, 1600);
}, 2700);
setTimeout(function() {
Snap.animate(pathLength4, 0, function( value ) {
path4.node.style.strokeDashoffset = value;
}, 1600);
}, 2700);
setTimeout(function() {
Snap.animate(pathLength5, 0, function( value ) {
path5.node.style.strokeDashoffset = value;
}, 1600);
}, 2700);
setTimeout(function() {
Snap.animate(pathLength6, 0, function( value ) {
path6.node.style.strokeDashoffset = value;
}, 1600);
}, 2700);
// get cordiante
function svgPoint(element, x, y) {
var pt = svg.createSVGPoint();
pt.x = x;
pt.y = y;
return pt.matrixTransform(element.getScreenCTM().inverse());
}
var svg = document.getElementById('svg');
var NS = svg.getAttribute('xmlns');
var local = svg.getElementById('Markets');
var coords = document.getElementById('coords');
svg.addEventListener('mousemove', function(e) {
var
x = e.clientX,
y = e.clientY,
svgP = svgPoint(svg, x, y),
svgL = svgPoint(local, x, y);
// output co-ordinates
coords.textContent =
'[page: ' + x + ',' + y +
'] => [svg space: ' + Math.round(svgP.x) + ',' + Math.round(svgP.y) +
'] [local transformed space: ' + Math.round(svgL.x) + ',' + Math.round(svgL.y) + ']'
;
}, false);
The solution here CODEPEN The Snap SVG is powerful library, I use it API. I'm not quite like my stuff with responsive solution.
I append the text tag to the link tag with such syntax, after that for each link set attributes with a link, and using the CSS opacity on hover it's possible to click the city name in SVG.
a.add(s.text(715,375, " LATVIA ").attr(linkStyle));
a.node.setAttributeNS('http://www.w3.org/1999/xlink', 'href', 'https://uk.wikipedia.org/wiki/%D0%9B%D0%B0%D1%82%D0%B2%D1%96%D1%8F');
.map {
height:450px;
max-height: 450px;
position: relative;
left: calc(100vw/2 - 100vh/2);
}
#media screen and (min-width: 640px){
.map {
height: 553px;
max-height: 553px;
left: 0;
left: calc(80vw/2 - 100vh/2);
}
}
#media screen and (min-width: 1200px){
.map {
height: 838px;
max-height: 838px;
left: 0;
}
}
I am using Raphael to make a simple clock for my website. I am trying to plot the seconds hand to make it tick like a normal clock.
Here is the code I am running:
window.onload = function() {
var paper = new Raphael(0, 0, 400, 400);
var backGround = paper.rect(0, 0, 400, 400);
backGround.attr({
fill: "orange"
});
var face = paper.circle(100, 100, 95);
face.attr({
"fill": "#f5f5f5",
"stroke": "#ff0000",
"stroke-width": "3"
})
var hand = paper.path("M100 110L100 25");
hand.attr({
stroke: "#ffff00",
"stroke-width": 1
});
function startTime() {
var hands = now.getSeconds();
hands = checkTime(hands);
hand.animate({
transform: ['r', ((hands * 6) - 180), 200, 200]
});
setTimeout(function() {
startTime()
}, 1000);
function checkTime(i) {
if (i < 10) {
i = "0" + i
};
return i;
}
startTime();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.4/raphael-min.js"></script>
You need to start the time function outside the function itself
You missed a new Date statement
I also moved the hands to 100x100
Removed the useless second formatter.
Changed the hand to black to see it better
window.onload = function() {
var paper = new Raphael(0, 0, 400, 400);
var backGround = paper.rect(0, 0, 400, 400);
backGround.attr({
fill: "orange"
});
var face = paper.circle(100, 100, 95);
face.attr({
"fill": "#f5f5f5",
"stroke": "#ff0000",
"stroke-width": "3"
})
var hand = paper.path("M100 110L100 25");
hand.attr({
stroke: "#000000",
"stroke-width": 1
});
function startTime() {
var now = new Date(),
hands = now.getSeconds();
hand.animate({
transform: ['r', ((hands * 6) - 180), 100, 100]
});
setTimeout(function() {
startTime()
}, 1000);
}
startTime();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.4/raphael-min.js"></script>
I am creating a little app with KineticJS which creates multiple nodes (RegularPolygons). After the stage is loaded (triggered with play(); ) I fill each node sequentially with an (pattern)image (triggered with loadImages(urls);.
This works fine, but now I want to add a nice fade-in effect with a Tween, like:
set all nodes > load single image > set node patternImage > tween to opacity 0.8 > on tween complete, load next image (repeat).
For some reason the tweens won't play, they just appear in complete state (opacity 1) ;(
var stage = new Kinetic.Stage({
container: 'canvas',
width: $(window).width(),
height: $(window).height()
});
var layer = new Kinetic.Layer()
var layer2 = new Kinetic.Layer()
var urls =
[
'http://lorempixel.com/300/300/sports/1',
'http://lorempixel.com/300/300/sports/2',
'http://lorempixel.com/300/300/sports/3',
'http://lorempixel.com/300/300/sports/4',
'http://lorempixel.com/300/300/sports/5',
'http://lorempixel.com/300/300/sports/6',
'http://lorempixel.com/300/300/sports/7',
'http://lorempixel.com/300/300/sports/8',
'http://lorempixel.com/300/300/sports/9',
'http://lorempixel.com/300/300/sports/10',
'http://lorempixel.com/300/300/business/1',
'http://lorempixel.com/300/300/business/2',
'http://lorempixel.com/300/300/business/3',
'http://lorempixel.com/300/300/business/4',
'http://lorempixel.com/300/300/business/5',
'http://lorempixel.com/300/300/business/6',
'http://lorempixel.com/300/300/business/7',
'http://lorempixel.com/300/300/business/8',
'http://lorempixel.com/300/300/business/9',
'http://lorempixel.com/300/300/business/10'/*,
'http://lorempixel.com/300/300/cats/1',
'http://lorempixel.com/300/300/cats/2',
'http://lorempixel.com/300/300/cats/3',
'http://lorempixel.com/300/300/cats/4',
'http://lorempixel.com/300/300/cats/5',
'http://lorempixel.com/300/300/cats/6',
'http://lorempixel.com/300/300/cats/7',
'http://lorempixel.com/300/300/cats/8',
'http://lorempixel.com/300/300/cats/9',
'http://lorempixel.com/300/300/cats/10',
'http://lorempixel.com/300/300/nature/1',
'http://lorempixel.com/300/300/nature/2',
'http://lorempixel.com/300/300/nature/3',
'http://lorempixel.com/300/300/nature/4',
'http://lorempixel.com/300/300/nature/5',
'http://lorempixel.com/300/300/nature/6',
'http://lorempixel.com/300/300/nature/7',
'http://lorempixel.com/300/300/nature/8',
'http://lorempixel.com/300/300/nature/9',
'http://lorempixel.com/300/300/nature/10',
'http://lorempixel.com/300/300/people/1',
'http://lorempixel.com/300/300/people/2',
'http://lorempixel.com/300/300/people/3',
'http://lorempixel.com/300/300/people/4',
'http://lorempixel.com/300/300/people/5'*/
];
// LOAD IMAGES
function loadImages(arrayOfImages, index) {
index = index || 0;
if (arrayOfImages && arrayOfImages.length && arrayOfImages.length > index) {
var img = new Image();
img.onload = function() {
var pane = layer2.get('#pane_' + index );
pane.fill('').fillPatternImage(img);
stage.draw(); // <<< THIS WORKS
var tween = new Kinetic.Tween({
node: pane,
duration: 1,
opacity: 0.8,
easing: Kinetic.Easings.BackEaseOut,
onFinish: function() {
loadImages(arrayOfImages, index + 1
}
}).play; // <<< NOT WORKING
//setTimeout(function(){ loadImages(arrayOfImages, index + 1); }, 200);
};
img.src = arrayOfImages[index];
}
}
function start() {
console.log("numOfPanes: " +urls.length);
for (i = 0; i <= urls.length; i++) {
var shape0 = new Kinetic.RegularPolygon({
x: i * 15,
y: i * 15,
sides: 5,
rotation: i * 10,
radius: 70,
fill: 'Red'
});
var shape1 = new Kinetic.RegularPolygon({
x: i * 15,
y: i * 15,
sides: 5,
rotation: i * 10,
radius: 70,
opacity: 0,
fillPatternOffset: {x:-220, y:70},
id: 'pane_' + i,
name: 'pane',
fill: 'Green'
});
layer.add(shape0);
layer2.add(shape1);
}
stage.add(layer,layer2);
}
// INIT
start();
// trigger loadimages() with console
loadImages(urls);
play is a function. So you should call it.
var tween = new Kinetic.Tween({
node: pane,
duration: 1,
opacity: 0.8,
easing: Kinetic.Easings.BackEaseOut,
onFinish: function() {
loadImages(arrayOfImages, index + 1
}
}).play();
Also: when you are finding node with get function it returns Collection. So if you need just node use:
var pane = layer2.get('#pane_' + index )[0];
Here's the fiddle: http://jsfiddle.net/G2VKx/3/
When I hover over the circle, the text shows up nicely, but if I remove the mouse, the THIRD time I mouseover, the height and width of the box that matches the dimensions of the text starts growing continuously and there's this weird stroboscopic effect.
var rsr = Raphael("holder", '1160', '1400');
xpos = 200;
ypos = 200;
rad = 50;
var rectext, el, txt;
cir = rsr.circle(xpos, ypos, rad);
cir.attr({
fill: '#444',
'fill-opacity': 1,
stroke: 'none'
});
cir.hover(function () {
this.animate({
r: rad * 1.2
}, 200);
recttext = rsr.set();
el = rsr.rect(0, 0, 0, 0, 3);
el.attr({
fill: '#fff'
});
txt = rsr.text(4, 10, "Title text").attr({
"text-anchor": "start",
fill: '#000000',
"font-size": 12
});
recttext.push(el);
recttext.push(txt);
var att = {
width: recttext.getBBox().width,
height: recttext.getBBox().height
};
el.attr(att);
recttext.translate(xpos - recttext.getBBox().width / 2, ypos - rad - 20);
}, function () {
this.animate({
r: rad
}, 100);
recttext.remove();
el.remove();
txt.remove();
});
Anto Jurković got it, using Raphael 2.1.2 instead of Fiddle's built-in 2.1.0 solved the problem.