I have a sprite that works as a button, but as you press it, it duplicates and lets you drag the duplicate. I've almost got it to work, however currently you have to click first to create a duplicate and are only able to drag after a second click. I'd like to be able to click once and immediately be able to drag.
It is important that the DUPLICATE gets dragged, as opposed to the original, since the original has all the click-functions.
create() {
sprite = scene.add.sprite(0,0, "clean", "showerhead_0000");
sprite.setInteractive({ useHandCursor: true });
sprite.on('pointerdown', () => {
dragTool(scene, options[idx], sprite);
});
this.input.on('drag', function(pointer, gameObject, dragX, dragY) {
gameObject.x = dragX;
gameObject.y = dragY;
});
this.input.on('dragend', function(pointer, gameObject) {
gameObject.destroy();
gameState.clean_cursor = false;
});
}
function dragTool(scene, option, sprite){
let activePointer = scene.input.activePointer;
let clone = scene.add.sprite(sprite.x,sprite.y, sprite.texture.key, sprite.frame.name);
gameState.clean_cursor = clone;
gameState.clean_cursor.setInteractive();
scene.input.setDraggable(gameState.clean_cursor);
}
Maybe I'm missing something, but a simple solutionwould be.
create two images/buttons, one normal and one as placeholder
the placeholder will be cover by the original, except when it is dragged
on drag, just drag the normal button
on the end of dragging reset the position of the normal button
this should work the same, and it is no so complicated.
Here a working example:
let Example = {
preload () {
this.load.spritesheet('brawler', 'https://labs.phaser.io/assets/animations/brawler48x48.png', { frameWidth: 48, frameHeight: 48 });
},
create () {
let buttonPos = { x:0, y:0};
let buttonPlaceholder = this.add.image(buttonPos.x, buttonPos.y, 'brawler', ).setOrigin(0, 0);
let button = this.add.image(buttonPos.x, buttonPos.y, 'brawler', ).setOrigin(0, 0);
this.isDragging = false;
button.setInteractive({ useHandCursor: true });
this.input.setDraggable(button);
this.input.on('drag', function(pointer, gameObject, dragX, dragY) {
gameObject.x = dragX;
gameObject.y = dragY;
});
this.input.on('dragend', function(pointer, gameObject) {
gameObject.x = buttonPos.x;
gameObject.y = buttonPos.y;
});
}
}
const config = {
type: Phaser.AUTO,
width: 400,
height: 200,
scene: [ Example ]
};
const game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
...this should be even more performant, than creating and destroying image/buttons...
Related
I'm fairly new to JS and am using swup.js (an awesome library btw) to transition between pages on a site. Transitions are working well however post-transition I need to re-initalise some JS functions I have.
According to swup.js docs I just to wrap them in a swup event. How would I do this in the below scenario? I think I specifically need to re-add the two functions to the two buttonLeft/buttonRight constants.
Examples here: https://swup.js.org/getting-started/reloading-javascript
//JS in script tag at bottom of body
const swup = new Swup();
init();
swup.on('contentReplaced', init);
// Function called in my main.js file
function init() {
if (document.querySelector('')) { // What do I include here?
// What do I include here?
}
// Functions I'd like reinitialized (also in main.js)
const buttonRight = document.getElementById('slide-right');
const buttonLeft = document.getElementById('slide-left');
buttonLeft.onclick = scrollLeft;
buttonRight.onclick = scrollRight;
function scrollLeft() {
document.getElementById('hoz-scroller').scrollTo({ left: 0, behavior: 'smooth' });
buttonLeft.classList.add('disabled');
if (buttonRight.classList.contains('disabled')) {
buttonRight.classList.remove('disabled');
}
};
function scrollRight() {
document.getElementById('hoz-scroller').scrollTo({ left: elementWidth, behavior: 'smooth' });
buttonRight.classList.add('disabled');
if (buttonLeft.classList.contains('disabled')) {
buttonLeft.classList.remove('disabled');
}
};
Answered my own question. Thanks to these guys: https://openclassrooms.com/en/courses/3523231-learn-to-code-with-javascript/4379006-use-constructor-functions. Answer for all those other noobs if they need it below:
Basically, you need to wrap the seperate functions in a new function and call that in the swup init function:
function init() {
if (document.querySelector('#hoz-scroller')) { //This checks if this element is on the the page (in the html)
new scrollers(); // If it is then it creates a new instance of the 'scrollers' function
}
}
var scrollStart = new scrollers(); // This initialises the below function the first time (first page load not controlled by swup)
function scrollers() { // This it the wrapper funtion
const element = document.getElementById("hoz-scroller");
const elementRect = element.getBoundingClientRect();
const elementWidth = elementRect.width;
const elementMiddle = (elementWidth / 2 - 50);
element.scrollTo(elementMiddle, 0);
const buttonRight = document.getElementById('slide-right');
const buttonLeft = document.getElementById('slide-left');
buttonLeft.onclick = scrollLeft;
buttonRight.onclick = scrollRight;
function scrollLeft() {
document.getElementById('hoz-scroller').scrollTo({ left: 0, behavior: 'smooth' });
buttonLeft.classList.add('disabled');
if (buttonRight.classList.contains('disabled')) {
buttonRight.classList.remove('disabled');
}
};
function scrollRight() {
document.getElementById('hoz-scroller').scrollTo({ left: elementWidth, behavior: 'smooth' });
buttonRight.classList.add('disabled');
if (buttonLeft.classList.contains('disabled')) {
buttonLeft.classList.remove('disabled');
}
};
};
Thanks to all who helped me out earlier tonight with my little project.
I am attempting to re-write a simple procedural program I wrote a few weeks ago using OOP javascript. The program is a reaction tester that presents a random shape to the user and measures how quickly the user clicks the shape and presents the speed. Earlier I finally managed to actually get a randomly sized and colored square to appear on the page. Now I am trying to write an event handler function that will set the css display property to none when the shape is clicked so that the shape disappears. However, the event handler function doesn't work and I have tried it a few different ways so far. See my entire code below:
function Shape () {
this.x = Math.floor(Math.random()*1200);
this.y = Math.floor(Math.random()*500);
this.draw();
}
Shape.prototype.draw = function() {
var shapeHtml = '<div id="shape-div"></div>';
var widthAndHeight = Math.floor(Math.random()*400);
this.shapeElement = $(shapeHtml);
this.shapeElement.css({
'width': widthAndHeight,
'height': widthAndHeight,
position: "relative",
left: this.x,
top: this.y
});
this.shapeElement.css({
display: "block"
});
//Just below is where I am trying to create a function to make the shape disappear when clicked
this.shapeElement.click(function() {
this.shapeElement.css("display", "none");
});
$("body").append(this.shapeElement);
}
"use strict";
Shape.prototype.colour = function() {
var colours = '0123456789ABCDEF'.split('');
var randomColour = "#";
for (i = 0; i < 6; i++) {
randomColour+=colours[Math.floor(Math.random()*16)];
};
this.shapeElement.css({backgroundColor: randomColour});
}
$(document).ready(function() {
var square = new Shape();
square.draw();
square.colour();
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
This just won't work. I am making the transition to OOP and finding it really difficult to do things that were a cinch using procedural programming. Is this typical? Thanks again for the help.
You could try replasing:
this.shapeElement.click(function() {...
for
this.shapeElement.on("click",function() {...
You are adding this element to the DOM after it loads.
Also, check your console, because inside the event listener this.shapeElement.css("display", "none"); probably gives you an error, this in that context is the element calling the event... I believe you could use:
$(this).css({"display": "none"});
Whenever you use function(){, the inside of that function acquires a new calling context (a new this) depending on how it was called. But in your handler, you don't want a new this value - you want to inherit the this of the instantiated Shape. You can use an arrow function instead, or you can remember that this on a handler references the clicked element:
function Shape () {
this.x = Math.floor(Math.random()*1200);
this.y = Math.floor(Math.random()*500);
this.draw();
}
Shape.prototype.draw = function() {
var shapeHtml = '<div id="shape-div"></div>';
var widthAndHeight = Math.floor(Math.random()*400);
this.shapeElement = $(shapeHtml);
this.shapeElement.css({
'width': widthAndHeight,
'height': widthAndHeight,
position: "relative",
left: this.x,
top: this.y
});
this.shapeElement.css({
display: "block"
});
//Just below is where I am trying to create a function to make the shape disappear when clicked
this.shapeElement.click(function() {
$(this).css("display", "none");
});
$("body").append(this.shapeElement);
}
"use strict";
Shape.prototype.colour = function() {
var colours = '0123456789ABCDEF'.split('');
var randomColour = "#";
for (i = 0; i < 6; i++) {
randomColour+=colours[Math.floor(Math.random()*16)];
};
this.shapeElement.css({backgroundColor: randomColour});
}
$(document).ready(function() {
var square = new Shape();
square.draw();
square.colour();
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I'm trying to add Caman.js filter functionality to a paper.js Raster. And in order not to loose image information I want to reset the image data to the original data (_camanImageData) every time before I call Caman by copying it from a cloned raster. The code is like this:
with(paper) {
Raster.prototype._camanImageData = null;
Raster.prototype.filter = function(options) {
var self = this,
ctx = this.getContext(), // caution! This also inits this._canvas (should).
size = this._size;
if( !this._camanOrgImage ) {
this._camanOrgImage = this.clone(false);
} else {
var dst = ctx.createImageData(size.width, size.height);
dst.data.set(this._camanOrgImage.getImageData(new Rectangle(0, 0, size.width, size.height)).data);
this.setImageData(dst);
}
Caman(this._canvas, function () {
for( option in options ) {
var value = options[option];
this[option](value);
}
this.render(function () {
self._changed(129);
});
});
};
}
The first call works fine. The second works apparently on the already modified data.
I call the filter function like this:
raster.filter({
brightness: 50,
saturation: 10,
hue: 20,
});
What do I do wrong?
The problem was with Caman. Caman doesn't reload the changed canvas, if a "data-caman-id" attribute is set in the canvas element. Just delete this before the call to Caman and it works fine:
this._canvas.removeAttribute("data-caman-id");
Caman(this._canvas, function () {
...
The solution came from: How can I change out an image using CamanJS?
I am building a web application and for this I am building a basic Image Editor using HTML5 and Raphael js. I have implemented some features like:
Resize Flip horizontal and/or vertical Drag
Some more features I need to implement are :
Crop Undo/Redo Changing background colour and opacity.
I am mainly looking for crop and undo functionality. Can anyone help me to achieve this using RaphaelJs and HTML5?
My Code:
var r, c, d;
var paper = Raphael("editor", 600,450);
var canvas = document.getElementById('editor');
canvas.style.backgroundColor='gray';
$scope.current;
var someFunction = function(e) {
e.data('dragged', false);
e.drag(dragmove, dragstart, dragend);
for(var i in elements) {
var thisShape = elements[i];
var ft = paper.freeTransform(thisShape,{draw:['bbox']});
ft.hideHandles();
thisShape.click(function(){
paper.forEach(function(el) {
if(el.freeTransform.handles.bbox != null)
el.freeTransform.hideHandles();
});
this.freeTransform.showHandles();
});
}
};
function dragmove(dx, dy, x, y, e) {
this.transform(this.current_transform+'T'+dx+','+dy);
}
function dragstart(x, y, e) {
this.current_transform = this.transform();
}
function dragend(e) {
this.current_transform = this.transform();
};
var elements = [];
$scope.raph = function () {
c= paper.circle(40,40,40).click(function() {
someFunction(this);
$scope.current= this;
});
r = paper.rect(50, 60, 100,100,25).click(function() {
someFunction(this);
$scope.current= this;
});
d = paper.rect(70, 60, 100,100,25).click(function() {
someFunction(this);
$scope.current= this;
});
elements.push(c,r,d);
c.attr({
fill:'red',
cursor:'pointer'
});
r.attr({
fill:'green',
cursor:'pointer'
});
d.attr({
fill:'blue',
cursor:'pointer'
});
};
$scope.back = function () {
$scope.current.toBack();
}
$scope.front = function () {
$scope.current.toFront();
}
$scope.clear = function () {
paper.clear();
};
$scope.flips = function () {
$scope.current.rotate(180);
};
//function for cloning element
$scope.clones = function () {
var n = $scope.current.clone();
n.attr("x",$scope.current.attr("x")+100);
$scope.current = n;
elements.push(n);
n.click(function() {
someFunction(this);
});
};
$scope.changing= function (e) {
$scope.opacity=event.target.value;
if($scope.current != null){
$scope.current.attr({
opacity:$scope.opacity
})
}
}
UPDATE: just use Fabric.js http://fabricjs.com/kitchensink/, although you'll probably need to add Darkroom http://mattketmo.github.io/darkroomjs/ for cropping.
Some suggestions for RaphaelJS:
resize: http://raphaeljs.com/reference.html#Element.transform c.transform('S1.5,1.5')
flip: resize with a negative scale in either direction (S-1,1)
drag: http://raphaeljs.com/reference.html#Element.drag, also consider set overloads like http://wesleytodd.com/2013/4/drag-n-drop-in-raphael-js.html
changing colors and stuff: http://raphaeljs.com/reference.html#Element.attr
undo -- you'll have to save a list of operations/previous states to reverse?
crop -- most likely you'd create the UI in raphael using a mask/overlay (layers: http://raphaeljs.com/reference.html#Element.toBack) to send instructions to a server-side API to handle the heavy work. You can manipulate raster images in the browser, but if people are uploading photos it could quickly overload the memory limitations (think: unedited 12MP photo)
Check out stuff like Sketchpad http://ianli.com/sketchpad/ or the commented Darkroom http://mattketmo.github.io/darkroomjs/ for ideas.
You can always try exploring the sourcecode for existing web editors, like Google+... (that was mostly a joke).
I'm trying to get a script to run but I'm having a little trouble. I've only used javascript once before, but now i have to make a character animation walk back and forth on a web page and continue until the page is closed. My debugger says there is a reference error on line 57, but i'm sure thats not the only problem. If anyone could take a look at the code and see if anything pops out at them, I would be grateful.
goog.provide('mysprites');
goog.require('lime');
goog.require('lime.Director');
goog.require('lime.Layer');
goog.require('lime.Sprite');
goog.require('lime.fill.Frame');
goog.require('lime.animation.KeyframeAnimation');
goog.require('lime.animation.MoveBy');
goog.require('lime.SpriteSheet');
goog.require('lime.animation.MoveTo');
goog.require('lime.animation.Sequence');
goog.require('lime.animation.Loop');
goog.require('lime.animation.Delay');
goog.require('lime.parser.JSON');
goog.require('lime.ASSETS.spaceman.json');
mysprites.WIDTH = 600;
mysprites.HEIGHT = 400;
mysprites.start = function() {
//director
mysprites.director = new lime.Director(document.body, mysprites.WIDTH, mysprites.HEIGHT);
mysprites.director.makeMobileWebAppCapable();
var gamescene = new lime.Scene;
layer = new lime.Layer();
gamescene.appendChild(layer);
// load the spritesheet
mysprites.ss = new lime.SpriteSheet('assets/spaceman.png',lime.ASSETS.spaceman.json,lime.parser.JSON);
var sprite = mysprites.makeMonster().setPosition(100,100);
layer.appendChild(sprite);
//move
var moveRight = new lime.animation.MoveTo(874, 100)
.setSpeed(1)
.setEasing(lime.animation.Easing.LINEAR);
var moveLeft = new lime.animation.MoveTo(100, 100)
.setSpeed(1)
.setEasing(lime.animation.Easing.LINEAR);
// show animation
var anim = new lime.animation.KeyframeAnimation();
anim.delay= 1/10;
for(var i=0;i<=9;i++){
anim.addFrame(mysprites.ss.getFrame('spaceman-'+'w'+'0'+i+'.png'));
}
monster.runAction(anim);
var anim2 = new lime.animation.KeyframeAnimation();
anim.delay= 1/10;
for(var i=0;i<=9;i++){
anim.addFrame(mysprites.ss.getFrame('spaceman-'+'e'+'0'+i+'.png'));
}
monster.runAction(anim2);
goog.events.listen(moveRight,lime.animation.Event.STOP, function () {
setTimeout(function () {
monster.runAction(moveLeft);
}, 500);
});
goog.events.listen(moveLeft,lime.animation.Event.STOP, function () {
setTimeout(function () {
monster.runAction(moveRight);
}, 500);
});
};
mysprites.makeMonster = function(){
var sprite = new lime.Sprite().setPosition(200,200)
.setFill(mysprites.ss.getFrame('spaceman-s00.png'));
//layer.appendChild(sprite);
return sprite;
};
goog.exportSymbol('mysprites.start', mysprites.start);
I think you'd better to ask your question here https://groups.google.com/forum/#!forum/limejs
Please check the following things:
If all the spritesheet items referenced in KeyframeAnimation are present in your spritesheet
Try to use setDelay, setLooping API methods instead of direct assignment
I do not see monster variable definition...