Raphael.js animate of image doesn't repaint in chrome - javascript

It seems to happen only when I try to animate an image on Chrome. All I want to do is make an image to move back an forth in Raphaël.js. I created a jsfiddle that illustrates the problem. I'm very sure that it used to work in Chrome since I use it to develop and it seems to be broken in later versions of Chrome. When I change the image to a rect for example it seems to render fine. When you resize the screen that contains the animation it repaints.
http://jsfiddle.net/k69yzz0o/1/
var moveForth = function () {
useControl.animate({x : 38, y: 0}, 900, moveBack);
};
var moveBack = function () {
useControl.animate({x : 0, y: 0}, 600, moveForth);
};
var R = Raphael("holder", 500, 500);
useControl = R.image("http://i.imgur.com/ta8zlD2.png", 0, 0, 189, 18);
moveForth();
It only happens in Chrome and I use latest Raphael.js 2.1.2.
How can I resolve this issue?

Yes, there appears to be an issue with Chrome recognizing the rect as dirty, and refusing to repaint it. You can see that it's working by mousing over the area.
I tried the same animation using a Transform rather than using the actual position. Transforming an object evidently correctly tells modern Chrome to redraw that area.
Here it is working:
var moveForth = function () {
useControl.animate({"transform":"T38,0"}, 900, ">", moveBack);
};
var moveBack = function () {
useControl.animate({"transform":""}, 600, "<", moveForth);
};
var R = Raphael("holder", 500, 500);
useControl = R.image("http://i.imgur.com/ta8zlD2.png", 0, 0, 189, 18);
moveForth();
I also added some easing (">") to give it a more natural bounce.

Related

Phaser 3: Change "Hitbox"/Interactive area of sprite without physics

The game I'm creating doesn't require any physics, however you are able to interact when hovering over/clicking on the sprite by using sprite.setInteractive({cursor: "pointer"});, sprite.on('pointermove', function(activePointer) {...}); and similar. However I noticed two issues with that:
The sprite has some area which are transparent. The interactive functions will still trigger when clicking on those transparent areas, which is unideal.
When playing a sprite animation, the interactive area doesn't seem to entirely (at all?) change, thus if the sprite ends on a frame bigger than the previous, there end up being small areas I can't interact with.
One option I thought of was to create a polygon over my sprite, which covers the area I want to be interactive. However before I do that, I simply wanted to ask if there are simpler ways to fix these issues.
Was trying to find an answer for this myself just now..
Think Make Pixel Perfect is what you're looking for.
this.add.sprite(x, y, key).setInteractive(this.input.makePixelPerfect());
https://newdocs.phaser.io/docs/3.54.0/focus/Phaser.Input.InputPlugin-makePixelPerfect
This might not be the best solution, but I would solve this problem like this. (If I don't want to use physics, and if it doesn't impact the performance too much)
I would check in the event-handler, if at the mouse-position the pixel is transparent or so, this is more exact and less work, than using bounding-boxes.
You would have to do some minor calculations, but it should work well.
btw.: if the origin is not 0, you would would have to compensate in the calculations for this. (in this example, the origin offset is implemented)
Here is a demo, for the click event:
let Scene = {
preload ()
{
this.load.spritesheet('brawler', 'https://labs.phaser.io/assets/animations/brawler48x48.png', { frameWidth: 48, frameHeight: 48 });
},
create ()
{
// Animation set
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('brawler', { frames: [ 0, 1, 2, 3 ] }),
frameRate: 8,
repeat: -1
});
// create sprite
const cody = this.add.sprite(200, 100).setOrigin(0);
cody.play('walk');
cody.setInteractive();
// just info text
this.mytext = this.add.text(10, 10, 'Click the Sprite, or close to it ...', { fontFamily: 'Arial' });
// event to watch
cody.on('pointerdown', function (pointer) {
// calculate x,y position of the sprite to check
let x = (pointer.x - cody.x) / (cody.displayWidth / cody.width)
let y = (pointer.y - cody.y) / (cody.displayHeight / cody.height);
// just checking if the properties are set
if(cody.anims && cody.anims.currentFrame){
let currentFrame = cody.anims.currentFrame;
let pixelColor = this.textures.getPixel(x, y, currentFrame.textureKey, currentFrame.textureFrame);
// alpha > 0 a visible pixel of the sprite, is clicked
if(pixelColor.a > 0) {
this.mytext.text = 'Hit';
} else {
this.mytext.text = 'No Hit';
}
// just reset the textmessage
setTimeout(_ => this.mytext.text = 'Click the Sprite, or close to it ...' , 1000);
}
}, this);
}
};
const config = {
type: Phaser.AUTO,
width: 400,
height: 200,
scene: Scene
};
const game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>

Javascript hover function

i am trying out raphael with javascript and trying to get the hang of it. At the moment i am trying to get it so that when the user hovers over a circle in raphael which i have drawn it will create a simmple hover messge like hi. However i am stuck to an end on this part, i cant seem to do anything about, any help on this would be great.
Javascript:
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(200,200,100);
face.attr({ fill: "Red"});
};
HTML
<div class= "bot">
<p>Hi there</p>
</div>
Just use the Element.hover() function, as documented on the Raphael website.
Update: The code snippet posted below works if you copy it onto your own computer and run it locally. However, to actually run this snippet on Stack Overflow, it only works on some browsers (e.g. it works on Firefox, but not on Chrome), probably due to the different browsers allowing or not allowing the code snippets to load external libraries, like Raphael.js, as required in this code snippet.
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(200, 200, 100);
face.attr({
fill: "Red"
});
face.hover(hoverHandler);
};
function hoverHandler() {
alert("hi");
}
<script src="http://github.com/DmitryBaranovskiy/raphael/raw/master/raphael-min.js"></script>
<div class="bot">
<p>Hi there</p>
</div>

Black image bug when exporting highcharts in Firefox

I am using highcharts to display several graphs on a webpage which display fine.
I have an export function that tries to combine the charts into a pdf. I am getting the svg of the chart and converting it to a jpeg image to be included in a pdf created by jsPDF.
Here is the code I am using to generate the images:
if ($('.chart').length > 0) {
var chartSVG = $('.chart').highcharts().getSVG(),
chartImg = new Image();
chartImg.src = "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(chartSVG)));
var chartCanvas = document.createElement("canvas");
chartCanvas.width = 600;
chartCanvas.height = 400;
chartCanvas.getContext("2d").drawImage(chartImg, 0, 0, 600, 400);
var chartImgData = chartCanvas.toDataURL("image/jpeg");
}
This works perfectly in Chrome but in Firefox it just returns a black image.
Does anyone know what might be going wrong or has seen a similar issue?
Thanks for your help.
UPDATE
I've updated the code but now no image is appended to the pdf document, either in Chrome or Firefox.
if ($('.sales').length > 0) {
var chartSVG = $('.sales').highcharts().getSVG(),
chartImg = new Image();
chartImg.onload = function () {
var chartCanvas = document.createElement("canvas");
chartCanvas.width = 600;
chartCanvas.height = 400;
chartCanvas.getContext("2d").drawImage(chartImg, 0, 0, 600, 400);
var chartImgData = chartCanvas.toDataURL("image/jpeg");
}
chartImg.src = "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(chartSVG)));
}
Not sure if I have the code in the correct place.
If I log 'chartImgData' to the console, both browsers generate a dataURI, but Firefox's version differs to Chromes.
UPDATE
Fixed the issue with black images. Now i'm struggling with how to return multiple images - how to nest multiple callbacks or is there another way?
Example: jsfiddle.net/wmuk489c/2
SOLVED
Thanks for your help #RobertLangson. fiddle updated with final working code should anyone need it: http://jsfiddle.net/wmuk489c/3/
FURTHER ISSUES:
My charts are dynamic and so may not always be present. I need to get an image from each graph that exists. If the graph does not exist, the 'getSVG' function fails, see example: http://jsfiddle.net/wmuk489c/4/
How should the img.onload work if the chart doesn't exist? The first chart in the callback may not be present either, so how would this work? Is there a better way to get the images?
setting chartImg.src causes an asynchronous load so you then need to do this...
chartImg.onload = function() {
var chartCanvas = document.createElement("canvas");
chartCanvas.width = 600;
chartCanvas.height = 400;
chartCanvas.getContext("2d").drawImage(chartImg, 0, 0, 600, 400);
var chartImgData = chartCanvas.toDataURL("image/jpeg");
doc.addImage(chartImgData, 'JPEG', 0, 0, 200, 100);
// You can only do this bit after you've added the image so it needs
// to be in the callback too
doc.save('test.pdf');
}
chartImg.src = ...
You've a race condition otherwise and I imagine you just happen to get away with it with the Chrome browser on your PC.
Here's your fiddle fixed.

Kinetic.js [How can I stop one sprite]

I´m starting with Kinetic.js and I have my first app working perfectly, but I don´t find the way to stop sprites... for example, I have one sprite and I want to "play" that sprite just once, see the frames and in the last one, one simple stop(). How can I do that? (Stop after the last "frame" of the sprite).
Here´s my code (for that sprite):
var myImage = new Image();
myImage.onload = function() {
var mySprite = new Kinetic.Sprite({
x: 250,
y: 40,
width: 70,
height: 109,
image: myImage,
animation: 'idle',
animations: animations,
frameRate: 5
});
layer1.add(mySprite);
mySprite.start();
};
myImage.src = 'sheet2.png';
Thanks in advance!
You can use the afterFrame function, which will enable you to trigger an event after a specific frame.
Then you can call stop() after frame 5, for example. (you'll need to update this to however many frames there are in your sprite.)
mySprite.afterFrame(5, function(){
mySprite.stop();
});
You can see an example of the use of afterFrame in the following two posts:
http://ramkulkarni.com/blog/sprite-animation-in-html5-canvas-with-kineticjs/
http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-sprite-tutorial/

Large SVG / Raphael circle distorts when being animated

I am animating a circle using Raphael. When the circle is large I get artifacts around the circle when its moving. It seems to be something of a clipping / redraw region issue and wondered if there was a work around?
It seems to be OK in firefox (if a little jerky) and appears very reliably in Chrome. It also is exacerbated by using opacity on the fill property i.e. rgba(255,0,0,0.7)
Here is a jsFiddle showing the issue. Just click around the paper on the right to move the circle.
Code:
var discattr = {
fill: "#666",
stroke: "none",
width: 35
};
var paper = Raphael("svgcontainer", 400, 400);
circle = paper.circle(150, 150, discattr.width, discattr.width).attr({
stroke: "none",
fill: "rgba(255,0,0,0.7)"
});
var coords = []
var animateCircle = function(coords) {
if (!coords.length) return;
var nextCoords = coords.shift()
var move = Raphael.animation(nextCoords, 500, "linear", function() {animateCircle(coords)});
circle.animate(move);
}
$("#svgcontainer").on("mouseup", function(e) {
coords.push({cx: e.pageX, cy: e.pageY})
animateCircle(coords);
});
Buffering is a technique used to prevent animation artifacts (tearing, as JamWaffles points out). If you look at the answer to this Stack Overflow question you'll find information about an SVG setting to turn on buffering, but so far it doesn't appear to be supported by major browsers.

Categories

Resources