EaselJS: Using updateCache() with AlphaMaskFilter When Dragging Mask - html5-canvas

I'm using an imported png with an alpha gradient that I'm setting as a mask that reveals the bitmap it is assigned to. The mask object is draggable (kind of like a flashlight). I know I'm supposed to use an AlphaMaskFilter as one of the filters, and I know I'm supposed to use .updateCache()... I'm just not sure I'm using them correctly?
var stage;
var assetQueue;
var bg;
var bgMask;
var container;
var amf;
$(document).ready(function(){
loadImages();
});
function loadImages()
{
// Set up preload queue
assetQueue = new createjs.LoadQueue();
assetQueue.addEventListener("complete", preloadComplete);
assetQueue.loadManifest([{id:"img_bg",src:"images/Nintendo-logo-red.jpg"}, {id:"img_bg_mask",src:"images/background_mask.png"}]);
}
function preloadComplete()
{
assetQueue.removeEventListener("complete", preloadComplete);
init();
}
function init()
{
stage = new createjs.Stage("stage_canvas");
setBackgrounds();
sizeStage();
$(document).mousemove(function(evt){
trackMouse(evt);
});
}
function trackMouse(evt)
{
var mouseX = evt.pageX;
var mouseY = evt.pageY;
// Move the containing clip around
container.x = mouseX - (bgMask.image.width / 2);
container.y = mouseY - (bgMask.image.height / 2);
// Offset the position of the masked image.
bg.x = -container.x;
bg.y = -container.y;
container.updateCache();
stage.update();
}
function setBackgrounds()
{
bg = new createjs.Bitmap(assetQueue.getResult("img_bg"));
bgMask = new createjs.Bitmap(assetQueue.getResult("img_bg_mask"));
container = new createjs.Container();
container.addChild(bg);
amf = new createjs.AlphaMaskFilter(bgMask.image)
container.filters = [amf];
container.cache(0, 0, bg.image.width, bg.image.height);
stage.addChild(container);
stage.update();
}
function sizeStage()
{
var windowW = 600;
var windowH = 600;
stage.canvas.width = windowW;
stage.canvas.height = windowH;
stage.update();
}

Solution found (for anyone interested). The key is to add the image you want to mask to a container. Move the container to any position you want, then offset the contained image within the container. The code has been updated to reflect this.

Related

How to dynamically change texture of PIXI.Sprite when PIXI.Sprite reaches certain position - Pixi.js?

I have a class which extends PIXI.Sprite. Here i create the sprite initially. The texture i use is a spritesheet and i create sprites from random sections of this spritesheet.png by creating random frames for the texture. There I add 10000 sprites and move them in random directions. Then I add the PIXI.Sprite class in another class which extends PIXI.ParticleContainer 10,000 times.
createTexture() {
this.textureWidth = 2048;
this.rectX = () => {
let number;
while (number % 32 !== 0) number = Math.floor(Math.random() * this.textureWidth) + 0;
return number;
}
this.rectY = () => {
let number;
while (number % 32 !== 0) number = Math.floor(Math.random() * 128) + 0;
return number;
}
this.initialTexture = PIXI.Texture.from(this.resources[assets.images[0].src].name);
this.rectangle = new PIXI.Rectangle(this.rectX(), this.rectY(), 32, 32);
this.initialTexture.frame = this.rectangle;
this.texture = new PIXI.Texture(this.initialTexture.baseTexture, this.initialTexture.frame);
this.texture.requiresUpdate = true;
this.texture.updateUvs();
this.timesChangedVy = 0;
}
When a Sprite hits window borders, i call the method change texture in the class of PIXI.Sprite:
changeTexture() {
let newTexture = PIXI.Texture.from(this.resources[assets.images[0].src].name);
let rectangle = new PIXI.Rectangle(this.rectX(), this.rectY(), 32, 32);
newTexture.frame = rectangle;
// this.texture.frame = rectangle
this.texture = newTexture;
// this.texture = new PIXI.Texture.from(this.resources[assets.images[0].src].name)
// this.texture._frame = rectangle
// this.texture.orig = rectangle
// this._texture = newTexture
// this.texture = new PIXI.Texture(newTexture.baseTexture, rectangle)
this.texture.update()
this.texture.requiresUpdate = true;
this.texture.updateUvs();
}
I tried different approaches. When i console.log the texture after changing it , i see that the frame and origins have been changed, but the new texture is not being rendered.
Does someone know where the problem lies and how i can fix it?
Finally, I found the reason for my sprites not updating on texture change.
It is because I add them as children of Pixi.ParticleContainer, which has less functionality than Pixi.Container and does not update Uvs of children by default.
THE SOLUTION IS TO SET uvs to true when creating PIXI.ParticleContainer.
It looks like this: new PIXI.ParticleContainer(10000, { uvs: true }).
This will solve the problem of changing textures not being updated and uvs will be uploaded and applied.
https://pixijs.download/dev/docs/PIXI.ParticleContainer.html

Three and Tween.js - unwanted spinning, is this gimbal lock?

I'm tweening an Object from here
Position (X:82.22, Y:-8.31, Z:57.75)
Rotation (X:-3.00, Y:-0.95, Z:-3.02)
to here
Position (X:57.36, Y:-8.31, Z:93.78)
Rotation (X:-3.05, Y:-0.55, Z:-3.10)
with this tween
behaviour.tween = new TWEEN.Tween(behaviour.origin).to(behaviour.target,behaviour.offsetTime * 1000)
.onUpdate(function(){
hotspot.position.x = behaviour.origin.pX;
hotspot.position.y = behaviour.origin.pY;
hotspot.position.z = behaviour.origin.pZ;
hotspot.rotation.x = behaviour.origin.rX;
hotspot.rotation.y = behaviour.origin.rY;
hotspot.rotation.z = behaviour.origin.rZ;
hotspot.scale.set(behaviour.origin.scale,behaviour.origin.scale,behaviour.origin.scale);
hotspot.opacity = behaviour.origin.opacity;
}).
And the object spins along the z axis as it moves across the scene.
Is this likely to be Gimbal lock? If so, what's the way to get around this?
It's always safer to use Quaternion for transitions. For me it works very well without using slerp, but just using Tween.js.
// excerpt from my code
var q = obj.quaternion.clone().multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), theta));
new TWEEN.Tween(obj.quaternion)
.to(q, time)
.onUpdate(function () { object.quaternion.copy(this); }) // onUpdate isn't really needed in this case
.start();
So, for you, it should work with something like this:
behaviour.origin.position = new THREE.Vector3(82.22, -8.31, 57.75);
behaviour.origin.quaternion = ew THREE.Quaternion().setFromEuler(new THREE.Euler(-3.00, -0.95, 3.02));
behaviour.target.position = new THREE.Vector(57.36, -8.31, 93.78);
behaviour.target.quaternion = ew THREE.Quaternion().setFromEuler(new THREE.Euler(3.05, -0.55, 3.10));
new TWEEN.Tween(behaviour.origin)
.to(behaviour.target, behaviour.offsetTime * 1000)
.onUpdate(function () {
hotspot.position.copy(behaviour.origin.position);
hotspot.quaternion.copy(behaviour.origin.quaternion);
})
.start();
UPDATE: Ok, it is adviced to use quaternion slerp(), otherwise it can result strange behaviours, too. Hence, I suppose, the code should look like this:
behaviour.origin.position = new THREE.Vector3(82.22, -8.31, 57.75);
behaviour.origin.quaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(-3.00, -0.95, 3.02));
behaviour.target.position = new THREE.Vector(57.36, -8.31, 93.78);
behaviour.target.quaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(3.05, -0.55, 3.10));
behaviour.origin.t = 0;
behaviour.target.t = 1;
new TWEEN.Tween(behaviour.origin)
.to(behaviour.target, behaviour.offsetTime * 1000)
.onUpdate(function () {
hotspot.position.copy(behaviour.origin.position);
THREE.Quaternion.slerp(behaviour.origin.quaternion, behaviour.target.quaternion, hotspot.quaternion, behaviour.origin.t);
})
.start();

UPDATED: Javascript logic to fix in a small function (SVG, obtaining absolute coords)

NEW:
So here is the code at codepen:
http://codepen.io/cmer41k/pen/pRJNww/
Currently function UpdateCoords(draggable) - is commented out in the code.
What I wanted is to update on mouseup event the coordinates of the path (circle as path here) to the absolute ones and remove transform attribute.
But I am failing to do that;(( sorry only learning
OLD:
In my code I have an svg element (path) that gets dragged around the root svg obj (svg) via transform="translate(x,y)" property.
I wanted to update such path element's attribute "d" (the string that describes all coords) to use absolute coordinates and get rid of transformed\translate thing.
Basically:
was: d="M10,10 30,10 20,30" + transform="translate(20,0);
to be: d="M30,10 50,10 40,30" + transform="translate(0,0)" (or if we can delete the transform - even better)
So I did the code that does the thing for me, but there is a bug that prevents proper result.
I am sure I am doing something wrong in here:
var v = Object.keys(path.controlPoints).length
// controlPoints here is just a place in path object where I store the coords for the path.
var matrix = path.transform.baseVal.consolidate();
//I validated that the above line does give me proper transform matrix with proper x,y translated values. Now below I am trying to loop through and update all control points (coordinates) of the path
for (i=0; i<v; i++) {
var position = svg.createSVGPoint();
position.x = path.controlPoints["p"+i].x;
position.y = path.controlPoints["p"+i].y;
// so for each of path's control points I create intermediate svgpoint that can leverage matrix data (or so I think) to "convert" old coords into the new ones.
position = position.matrixTransform(matrix);
path.controlPoints["p"+i].x = position.x;
path.controlPoints["p"+i].y = position.y;
}
// I am sure I am doing something wrong here, maybe its because I am not "cleaning"/resetting this position thing in this loop or smth?
Sorry I am not a programmer, just learning stuff and the question is - in this code snipped provided the goal that I described - is something wrong with how I handle "position"?
Alright, the code snipped is now functioning properly!
So after I figured how to obtain properly the matrix I still had a weird displacement for any subsequent draggables.
I became clear that those displacements happen even before my function.
I debugged it a bit and realized that I was not clearing the ._x and ._y params that I use for dragging.
Now code works!
http://codepen.io/cmer41k/pen/XpbpQJ
var svgNS = "http://www.w3.org/2000/svg";
var draggable = null;
var canvas = {};
var inventory = {};
var elementToUpdate = {};
//debug
var focusedObj = {};
var focusedObj2 = {};
// to be deleted
window.onload = function() {
canvas = document.getElementById("canvas");
inventory = document.getElementById("inventory");
AddListeners();
}
function AddListeners() {
document.getElementById("svg").addEventListener("mousedown", Drag);
document.getElementById("svg").addEventListener("mousemove", Drag);
document.getElementById("svg").addEventListener("mouseup", Drag);
}
// Drag function //
function Drag(e) {
var t = e.target, id = t.id, et = e.type; m = MousePos(e); //MousePos to ensure we obtain proper mouse coordinates
if (!draggable && (et == "mousedown")) {
if (t.className.baseVal=="inventory") { //if its inventory class item, this should get cloned into draggable
copy = t.cloneNode(true);
copy.onmousedown = copy.onmouseup = copy.onmousemove = Drag;
copy.removeAttribute("id");
copy._x = 0;
copy._y = 0;
canvas.appendChild(copy);
draggable = copy;
dPoint = m;
}
else if (t.className.baseVal=="draggable") { //if its just draggable class - it can be dragged around
draggable = t;
dPoint = m;
}
}
// drag the spawned/copied draggable element now
if (draggable && (et == "mousemove")) {
draggable._x += m.x - dPoint.x;
draggable._y += m.y - dPoint.y;
dPoint = m;
draggable.setAttribute("transform", "translate(" +draggable._x+","+draggable._y+")");
}
// stop drag
if (draggable && (et == "mouseup")) {
draggable.className.baseVal="draggable";
UpdateCoords(draggable);
console.log(draggable);
draggable._x = 0;
draggable._y = 0;
draggable = null;
}
}

Clicking or hovering over multiple images in canvas?

I am working on a project for my programming class. I'd like to essentially have a canvas with a background element (say a room.jpg) and then maybe three interactive objects in the room (lamp.jpg, couch.jpg, desk.jpg). I'd like for it to be that if you hover over the lamp a small box or text pops out, giving you some information. Or maybe have it so if you click an image, the same concept happens. You know, something interactive with the objects in the canvas. Again, I'm new to canvas but we have to use it in our assignment. My current code is:
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
var sources = {
room: 'room.jpg',
title: 'title.jpg'
};
loadImages(sources, function(images) {
context.drawImage(images.room, 0,0);
context.drawImage(images.title, 0,0);
});
}
But from what I understand, it makes the two jpegs a "permanent" canvas element (unable to be messed with). I had been trying to get it so that when I clicked I'd go from the title.jpg to the room.jpg but I've since given up. Essentially, all I want now is just to have the room.jpg appear when the page is first loaded, and have three other png objects on top (as objects in the room). Are these able to be interacted with, or do I have to put the images into the canvas in another way? Thanks for all your help and your patience!
// --- Image Loader ----
var images = {};
var loadedImages = 0;
var pictures = {
room: 'room.jpg',
title: 'title.jpg'
lamp1: 'lampoff.jpg'
lamp2: 'lampon.jpg'
};
function loadImages(sources, callback) {
var numImages = 0;
for(var src in sources)numImages++;
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
// --- Mouse Down Functionality ----
$('#canvas').addEventListener('mouseDown', function(e){
if(e.clientX){
var rect = this.getBoundingClientRect();
if(rect) clickCanvas(e.clientX - rect.left, e.clientY - rect.top)
else clickCanvas(e.clientX - this.offsetLeft, e.clientY - this.offsetTop);
}else if(e.offsetX) clickCanvas(e.offsetX, e.offsetY);
else if(e.layerX) clickCanvas(e.layerX, e.layerY);
else console.warn("Couldn't Determine Mouse Coordinates");
})
var lampOn;
function drawCanvas(showLamp){
lampOn = showLamp;
canvas.width = canvas.width //clears canvas
context.drawImage(images.room, 0,0);
context.drawImage(images.title, 0,0);
if(lampOn){
context.drawImage(images.lamp2, 100,100);
}else{
context.drawImage(images.lamp1, 100,100);
}
}
function clickCanvas(x,y){
console.log('clicked canvas at:',x,y)
if(clickedLamp){
drawCanvas(!lampOn)
}
}
loadImages(pictures, function(images) {
drawCanvas(false)
});
Make sure to replace "clickedLamp" and "#canvas"! The idea here is that you redraw the same canvas, using the same function. Everytime you modify any of it, you rerender ALL of it. See if you can get this example working, it will help clarify alot. If you don't understand something comment

removeAllChildren is not working in createjs

I'm trying to change the bitmap image in createjs and want to remove all children in a container when reset button is clicked. But removeAllChildren is not working in me.
function drawPhoneImage() {
stage = new createjs.Stage('canvas');
container = new createjs.Container();
phone = new createjs.Bitmap(phoneImg);
phone.x = 268;
phone.y = 64;
stage.addChild(container);
container.addChild(phone);
stage.update();
phone.addEventListener("click", function() {
console.log('phone clicked');
createjs.Ticker.addEventListener("tick", movePhoneImage);
});
}
function movePhoneImage(event) {
phone.x -=10;
if(phone.x < 156) {
phone.x =156;
showPhoneSnap();
}
stage.update(event);
}
Then after clicking the phone object, I'll need to replace it with another bitmap(which works):
function showPhoneSnap() {
snap = new createjs.Bitmap(snapImg);
snap.x = 156;
snap.y = 64;
container.removeAllChildren();
container.addChild(snap);
stage.update();
}
At first, removeAllChildren is working in the first child of the container, but when i tried resetting the stage after adding another bitmap in the container..removeAllChildren() is not working.
function resetStage() {
container.removeAllChildren();
stage.update();
}
I'm having a hard time solving this issue, thanks for anyone who can help.
Make sure that "snapImg" is an image that is loaded.
snap = new createjs.Bitmap(snapImg);
The issue is that you are not updating the stage when the image is loaded.
var image = new Image();
image.src = "path";
image.onload = showPhoneSnap;
function showPhoneSnap() {
//This will ensure that the image is ready.
var bmp = new createjs.Bitmap(this);
...
stage.update();
}

Resources