Changing Transition Timing of a CodePen - javascript

I really like the CodePen linked here, and would like to incorporate it into a personal project I'm working on, however I don't know JS and want to change the fly-in effect. I just want the entire transition to fade into view as soon as the webpage loads. Is there any way anyone could help me do this?
console.clear();
const { gsap, imagesLoaded } = window;
const buttons = {
prev: document.querySelector(".btn--left"),
next: document.querySelector(".btn--right"),
};
const cardsContainerEl = document.querySelector(".cards__wrapper");
const appBgContainerEl = document.querySelector(".app__bg");
const cardInfosContainerEl = document.querySelector(".info__wrapper");
buttons.next.addEventListener("click", () => swapCards("right"));
buttons.prev.addEventListener("click", () => swapCards("left"));
function swapCards(direction) {
const currentCardEl = cardsContainerEl.querySelector(".current--card");
const previousCardEl = cardsContainerEl.querySelector(".previous--card");
const nextCardEl = cardsContainerEl.querySelector(".next--card");
const currentBgImageEl = appBgContainerEl.querySelector(".current--image");
const previousBgImageEl = appBgContainerEl.querySelector(".previous--image");
const nextBgImageEl = appBgContainerEl.querySelector(".next--image");
changeInfo(direction);
swapCardsClass();
removeCardEvents(currentCardEl);
function swapCardsClass() {
currentCardEl.classList.remove("current--card");
previousCardEl.classList.remove("previous--card");
nextCardEl.classList.remove("next--card");
currentBgImageEl.classList.remove("current--image");
previousBgImageEl.classList.remove("previous--image");
nextBgImageEl.classList.remove("next--image");
currentCardEl.style.zIndex = "50";
currentBgImageEl.style.zIndex = "-2";
if (direction === "right") {
previousCardEl.style.zIndex = "20";
nextCardEl.style.zIndex = "30";
nextBgImageEl.style.zIndex = "-1";
currentCardEl.classList.add("previous--card");
previousCardEl.classList.add("next--card");
nextCardEl.classList.add("current--card");
currentBgImageEl.classList.add("previous--image");
previousBgImageEl.classList.add("next--image");
nextBgImageEl.classList.add("current--image");
} else if (direction === "left") {
previousCardEl.style.zIndex = "30";
nextCardEl.style.zIndex = "20";
previousBgImageEl.style.zIndex = "-1";
currentCardEl.classList.add("next--card");
previousCardEl.classList.add("current--card");
nextCardEl.classList.add("previous--card");
currentBgImageEl.classList.add("next--image");
previousBgImageEl.classList.add("current--image");
nextBgImageEl.classList.add("previous--image");
}
}
}
function changeInfo(direction) {
let currentInfoEl = cardInfosContainerEl.querySelector(".current--info");
let previousInfoEl = cardInfosContainerEl.querySelector(".previous--info");
let nextInfoEl = cardInfosContainerEl.querySelector(".next--info");
gsap.timeline()
.to([buttons.prev, buttons.next], {
duration: 0.2,
opacity: 0.5,
pointerEvents: "none",
})
.to(
currentInfoEl.querySelectorAll(".text"),
{
duration: 0.4,
stagger: 0.1,
translateY: "-120px",
opacity: 0,
},
"-="
)
.call(() => {
swapInfosClass(direction);
})
.call(() => initCardEvents())
.fromTo(
direction === "right"
? nextInfoEl.querySelectorAll(".text")
: previousInfoEl.querySelectorAll(".text"),
{
opacity: 0,
translateY: "40px",
},
{
duration: 0.4,
stagger: 0.1,
translateY: "0px",
opacity: 1,
}
)
.to([buttons.prev, buttons.next], {
duration: 0.2,
opacity: 1,
pointerEvents: "all",
});
function swapInfosClass() {
currentInfoEl.classList.remove("current--info");
previousInfoEl.classList.remove("previous--info");
nextInfoEl.classList.remove("next--info");
if (direction === "right") {
currentInfoEl.classList.add("previous--info");
nextInfoEl.classList.add("current--info");
previousInfoEl.classList.add("next--info");
} else if (direction === "left") {
currentInfoEl.classList.add("next--info");
nextInfoEl.classList.add("previous--info");
previousInfoEl.classList.add("current--info");
}
}
}
function updateCard(e) {
const card = e.currentTarget;
const box = card.getBoundingClientRect();
const centerPosition = {
x: box.left + box.width / 2,
y: box.top + box.height / 2,
};
let angle = Math.atan2(e.pageX - centerPosition.x, 0) * (35 / Math.PI);
gsap.set(card, {
"--current-card-rotation-offset": `${angle}deg`,
});
const currentInfoEl = cardInfosContainerEl.querySelector(".current--info");
gsap.set(currentInfoEl, {
rotateY: `${angle}deg`,
});
}
function resetCardTransforms(e) {
const card = e.currentTarget;
const currentInfoEl = cardInfosContainerEl.querySelector(".current--info");
gsap.set(card, {
"--current-card-rotation-offset": 0,
});
gsap.set(currentInfoEl, {
rotateY: 0,
});
}
function initCardEvents() {
const currentCardEl = cardsContainerEl.querySelector(".current--card");
currentCardEl.addEventListener("pointermove", updateCard);
currentCardEl.addEventListener("pointerout", (e) => {
resetCardTransforms(e);
});
}
initCardEvents();
function removeCardEvents(card) {
card.removeEventListener("pointermove", updateCard);
}
function init() {
let tl = gsap.timeline();
tl.to(cardsContainerEl.children, {
delay: 0.15,
duration: 0.5,
stagger: {
ease: "power4.inOut",
from: "right",
amount: 0.1,
},
"--card-translateY-offset": "0%",
})
.to(cardInfosContainerEl.querySelector(".current--info").querySelectorAll(".text"), {
delay: 0.5,
duration: 0.4,
stagger: 0.1,
opacity: 1,
translateY: 0,
})
.to(
[buttons.prev, buttons.next],
{
duration: 0.4,
opacity: 1,
pointerEvents: "all",
},
"-=0.4"
);
}
const waitForImages = () => {
const images = [...document.querySelectorAll("img")];
const totalImages = images.length;
let loadedImages = 0;
const loaderEl = document.querySelector(".loader span");
gsap.set(cardsContainerEl.children, {
"--card-translateY-offset": "100vh",
});
gsap.set(cardInfosContainerEl.querySelector(".current--info").querySelectorAll(".text"), {
translateY: "40px",
opacity: 0,
});
gsap.set([buttons.prev, buttons.next], {
pointerEvents: "none",
opacity: "0",
});
images.forEach((image) => {
imagesLoaded(image, (instance) => {
if (instance.isComplete) {
loadedImages++;
let loadProgress = loadedImages / totalImages;
gsap.to(loaderEl, {
duration: 1,
scaleX: loadProgress,
backgroundColor: `hsl(${loadProgress * 120}, 100%, 50%`,
});
if (totalImages == loadedImages) {
gsap.timeline()
.to(".loading__wrapper", {
duration: 0.8,
opacity: 0,
pointerEvents: "none",
})
.call(() => init());
}
}
});
});
};
waitForImages();
Here's the code I'm working with. I have tried altering the --card-transition-duration variable to less than 800ms, but this doesn't seem to have the effect that I want since it is used so often throughout the entire CodePen.

Related

Javascript not connected to CDN link?

I was going to make changes on the spinning app off of the website and tested it on JSFIDDLE. It WORKS, BUT on my local host, I'm still getting Uncaught ReferenceError: MotionPathPlugin is not defined on console.
I have tried to put these CDNs
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/MotionPathPlugin.min.js"></script>
<script>
gsap.registerPlugin(MotionPathPlugin);
const circlePath = MotionPathPlugin.convertToPath("#holder", false)[0];
circlePath.id = "circlePath";
document.querySelector("svg").prepend(circlePath);
let items = gsap.utils.toArray(".item"),
numItems = items.length,
itemStep = 1 / numItems,
wrapProgress = gsap.utils.wrap(0, 1),
snap = gsap.utils.snap(itemStep),
wrapTracker = gsap.utils.wrap(0, numItems),
tracker = { item: 0 };
gsap.set(items, { motionPath: {
path: circlePath,
align: circlePath,
alignOrigin: [0.5, 0.5],
end: i => i / items.length
}, scale: 0.9
});
const tl = gsap.timeline({ paused:true, reversed: true });
tl.to('.wrapper', {
rotation: 360,
transformOrigin: 'center',
duration: 1,
ease: 'none'
});
tl.to(items, {
rotation: "-=360",
transformOrigin: 'center',
duration: 1,
ease: 'none',
}, 0);
tl.to(tracker, {
item: numItems,
duration: 1,
ease: 'none',
modifiers: {
item(value) {
return wrapTracker(numItems - Math.round(value))
}
}
}, 0);
items.forEach(function (el, i) {
el.addEventListener("click", function () {
var current = tracker.item,
activeItem = i;
if (i === current) {
return;
}
//set active item to the item that was clicked and remove active
class from all items
document.querySelector('.item.active').classList.remove('active');
items[activeItem].classList.add('active');
var diff = current - i;
if (Math.abs(diff) < numItems / 2) {
moveWheel(diff * itemStep);
} else {
var amt = numItems - Math.abs(diff);
if (current > i) {
moveWheel(amt * -itemStep);
} else {
moveWheel(amt * itemStep);
}
}
});
});
document.getElementById('next').addEventListener("click", function () {
return moveWheel(-itemStep);
});
document.getElementById('prev').addEventListener("click", function
() {
return moveWheel(itemStep);
});
function moveWheel(amount, i, index) {
let progress = tl.progress();
tl.progress(wrapProgress(snap(tl.progress() + amount)))
let next = tracker.item;
tl.progress(progress);
document.querySelector('.item.active').classList.remove('active');
items[next].classList.add('active');
gsap.to(tl, {
progress: snap(tl.progress() + amount),
modifiers: {
progress: wrapProgress
}
});
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/MotionPathPlugin.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/MotionPathPlugin.min.js"></script>
<script>
gsap.registerPlugin(MotionPathPlugin);
const circlePath = MotionPathPlugin.convertToPath("#holder", false)[0];
circlePath.id = "circlePath";
document.querySelector("svg").prepend(circlePath);
let items = gsap.utils.toArray(".item"),
numItems = items.length,
itemStep = 1 / numItems,
wrapProgress = gsap.utils.wrap(0, 1),
snap = gsap.utils.snap(itemStep),
wrapTracker = gsap.utils.wrap(0, numItems),
tracker = {
item: 0
};
gsap.set(items, {
motionPath: {
path: circlePath,
align: circlePath,
alignOrigin: [0.5, 0.5],
end: i => i / items.length
},
scale: 0.9
});
const tl = gsap.timeline({
paused: true,
reversed: true
});
tl.to('.wrapper', {
rotation: 360,
transformOrigin: 'center',
duration: 1,
ease: 'none'
});
tl.to(items, {
rotation: "-=360",
transformOrigin: 'center',
duration: 1,
ease: 'none',
}, 0);
tl.to(tracker, {
item: numItems,
duration: 1,
ease: 'none',
modifiers: {
item(value) {
return wrapTracker(numItems - Math.round(value))
}
}
}, 0);
items.forEach(function(el, i) {
el.addEventListener("click", function() {
var current = tracker.item,
activeItem = i;
if (i === current) {
return;
}
//set active item to the item that was clicked and remove active
class from all items
document.querySelector('.item.active').classList.remove('active');
items[activeItem].classList.add('active');
var diff = current - i;
if (Math.abs(diff) < numItems / 2) {
moveWheel(diff * itemStep);
} else {
var amt = numItems - Math.abs(diff);
if (current > i) {
moveWheel(amt * -itemStep);
} else {
moveWheel(amt * itemStep);
}
}
});
});
document.getElementById('next').addEventListener("click", function() {
return moveWheel(-itemStep);
});
document.getElementById('prev').addEventListener("click", function() {
return moveWheel(itemStep);
});
function moveWheel(amount, i, index) {
let progress = tl.progress();
tl.progress(wrapProgress(snap(tl.progress() + amount)))
let next = tracker.item;
tl.progress(progress);
document.querySelector('.item.active').classList.remove('active');
items[next].classList.add('active');
gsap.to(tl, {
progress: snap(tl.progress() + amount),
modifiers: {
progress: wrapProgress
}
});
}
</script>
In the head tag - no luck!
In the body tag - no luck!
In the bottom of body tag - no luck!
I'm not sure what's going on.
This can be resololved one of two ways.
Re-arrange your code
Make your code execute after the CDN import
Side note: this was a syntax issue: class from all items and should be // class from all items
Here are each of those examples.
Re-arrange / order the code
IMHO this is less desireable but often done/recommended but can be fragile:
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/MotionPathPlugin.min.js"></script>
<div> I have errors since there is no related HTML referenced in the code! </div>
<script>
gsap.registerPlugin(MotionPathPlugin);
const circlePath = MotionPathPlugin.convertToPath("#holder", false)[0];
circlePath.id = "circlePath";
document.querySelector("svg").prepend(circlePath);
let items = gsap.utils.toArray(".item"),
numItems = items.length,
itemStep = 1 / numItems,
wrapProgress = gsap.utils.wrap(0, 1),
snap = gsap.utils.snap(itemStep),
wrapTracker = gsap.utils.wrap(0, numItems),
tracker = {
item: 0
};
gsap.set(items, {
motionPath: {
path: circlePath,
align: circlePath,
alignOrigin: [0.5, 0.5],
end: i => i / items.length
},
scale: 0.9
});
const tl = gsap.timeline({
paused: true,
reversed: true
});
tl.to('.wrapper', {
rotation: 360,
transformOrigin: 'center',
duration: 1,
ease: 'none'
});
tl.to(items, {
rotation: "-=360",
transformOrigin: 'center',
duration: 1,
ease: 'none',
}, 0);
tl.to(tracker, {
item: numItems,
duration: 1,
ease: 'none',
modifiers: {
item(value) {
return wrapTracker(numItems - Math.round(value))
}
}
}, 0);
items.forEach(function(el, i) {
el.addEventListener("click", function() {
var current = tracker.item,
activeItem = i;
if (i === current) {
return;
}
//set active item to the item that was clicked and remove active
// class from all items
document.querySelector('.item.active').classList.remove('active');
items[activeItem].classList.add('active');
var diff = current - i;
if (Math.abs(diff) < numItems / 2) {
moveWheel(diff * itemStep);
} else {
var amt = numItems - Math.abs(diff);
if (current > i) {
moveWheel(amt * -itemStep);
} else {
moveWheel(amt * itemStep);
}
}
});
});
document.getElementById('next').addEventListener("click", function() {
return moveWheel(-itemStep);
});
document.getElementById('prev').addEventListener("click", function() {
return moveWheel(itemStep);
});
function moveWheel(amount, i, index) {
let progress = tl.progress();
tl.progress(wrapProgress(snap(tl.progress() + amount)))
let next = tracker.item;
tl.progress(progress);
document.querySelector('.item.active').classList.remove('active');
items[next].classList.add('active');
gsap.to(tl, {
progress: snap(tl.progress() + amount),
modifiers: {
progress: wrapProgress
}
});
}
</script>
Make the code run after it loads the CDN code
<div> I have errors since there is no related HTML referenced in the code! </div>
<script>
var loadRun = function() {
gsap.registerPlugin(MotionPathPlugin);
const circlePath = MotionPathPlugin.convertToPath("#holder", false)[0];
circlePath.id = "circlePath";
document.querySelector("svg").prepend(circlePath);
let items = gsap.utils.toArray(".item"),
numItems = items.length,
itemStep = 1 / numItems,
wrapProgress = gsap.utils.wrap(0, 1),
snap = gsap.utils.snap(itemStep),
wrapTracker = gsap.utils.wrap(0, numItems),
tracker = {
item: 0
};
gsap.set(items, {
motionPath: {
path: circlePath,
align: circlePath,
alignOrigin: [0.5, 0.5],
end: i => i / items.length
},
scale: 0.9
});
const tl = gsap.timeline({
paused: true,
reversed: true
});
tl.to('.wrapper', {
rotation: 360,
transformOrigin: 'center',
duration: 1,
ease: 'none'
});
tl.to(items, {
rotation: "-=360",
transformOrigin: 'center',
duration: 1,
ease: 'none',
}, 0);
tl.to(tracker, {
item: numItems,
duration: 1,
ease: 'none',
modifiers: {
item(value) {
return wrapTracker(numItems - Math.round(value))
}
}
}, 0);
items.forEach(function(el, i) {
el.addEventListener("click", function() {
var current = tracker.item,
activeItem = i;
if (i === current) {
return;
}
//set active item to the item that was clicked and remove active
// class from all items
document.querySelector('.item.active').classList.remove('active');
items[activeItem].classList.add('active');
var diff = current - i;
if (Math.abs(diff) < numItems / 2) {
moveWheel(diff * itemStep);
} else {
var amt = numItems - Math.abs(diff);
if (current > i) {
moveWheel(amt * -itemStep);
} else {
moveWheel(amt * itemStep);
}
}
});
});
document.getElementById('next').addEventListener("click", function() {
return moveWheel(-itemStep);
});
document.getElementById('prev').addEventListener("click", function() {
return moveWheel(itemStep);
});
function moveWheel(amount, i, index) {
let progress = tl.progress();
tl.progress(wrapProgress(snap(tl.progress() + amount)))
let next = tracker.item;
tl.progress(progress);
document.querySelector('.item.active').classList.remove('active');
items[next].classList.add('active');
gsap.to(tl, {
progress: snap(tl.progress() + amount),
modifiers: {
progress: wrapProgress
}
});
}
};
window.onload = loadRun;
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/MotionPathPlugin.min.js"></script>

Implementing bullet collisions in Matter.js for a shooting game

I am trying to make a shooting game in matter.js but can't find a way to shoot bullets from the player's exact location and how to count the collision between player and bullet but not with the walls.
I want to fire a bullet from player1 and then on pressing D again it should fire another bullet from the player1's last position.
My Codepen of this game
let p1= Matter.Bodies.polygon(200, 200, 3, 40, {
chamfer: {
radius: [15,10,15]
},
isStatic: false,
inertia: Infinity,
friction: 0.9,
render: {
fillStyle: '#F9ED69'
},
mass:1
});
let p2 = Matter.Bodies.polygon(1100, 200, 3, 40, {
chamfer: {
radius: [15,10,15]
},
isStatic: false,
inertia: Infinity,
friction: 0.9,
render: {
fillStyle: '#11999E'
},
mass:1
});
let bullet1 = Matter.Bodies.polygon(400, 300, 3, 7, {
chamfer: {
radius: [4,2,4]
},
isStatic: false,
inertia: Infinity,
friction: 0.9,
render: {
fillStyle: '#F9ED69'
},
mass:0
});
const keyHandlers = {
KeyS: () => {
Matter.Body.applyForce(p1, {
x: p1.position.x,
y: p1.position.y
}, {x: 0.0, y: 0.001})
},
KeyW: () => {
Matter.Body.applyForce(p1, {
x: p1.position.x,
y: p1.position.y
}, {x: 0.0, y: -0.002})
},
KeyD:()=>{
Matter.Body.applyForce(bullet1, {
x: p1.position.x,
y: p1.position.y
}, {x: 0.001, y: 0.0})
},
};
const keysDown = new Set();
document.addEventListener("keydown", event => {
keysDown.add(event.code);
});
document.addEventListener("keyup", event => {
keysDown.delete(event.code);
});
Matter.Events.on(engine, "beforeUpdate", event => {
[...keysDown].forEach(k => {
keyHandlers[k]?.();
});
});
// on collision of a bullet with wall and other bodies remove the bullet from the world after some delay and add the score
let score1 = 0;
let score2 = 0;
let health
Matter.Events.on(engine, "collisionStart", event => {
for (let i = 0; i < event.pairs.length; i++) {
const pair = event.pairs[i];
if (pair.bodyA === bullet1 || pair.bodyB === bullet1) {
Matter.World.remove(engine.world, bullet1);
alert('1');
}
if (pair.bodyA === bullet2 || pair.bodyB === bullet2) {
Matter.World.remove(engine.world, bullet2);
alert('2');
}
}
score1++;
alert(`SCore1 is ${score1}`); // these alerts are just to confirm the collision
});
You're on the right track, but if you hardcode bullet1 and bullet2 you're stuck with just those two bullets. Even with a fixed number of bullets and re-using the bodies (good for performance but maybe premature optimization), I'd probably use an array to store these bullets, which is almost always the correct move after you catch yourself doing thing1, thing2...
Here's a proof of concept. I'm creating and destroying bullets here to keep the coding easier, but it'd be more performant to keep a pool of objects and re-use/re-position them.
I'm also using sets to keep track of the types of the bodies, but you might want to use labels. Most of the code here could go in many different directions, specific to your use case.
const engine = Matter.Engine.create();
engine.gravity.y = 0; // enable top-down
const map = {width: 300, height: 300};
const render = Matter.Render.create({
element: document.body,
engine,
options: {...map, wireframes: false},
});
const player = {
score: 0,
body: Matter.Bodies.polygon(
map.width / 2, map.height / 2, 3, 15, {
frictionAir: 0.06,
density: 0.9,
render: {fillStyle: "red"},
},
),
lastShot: Date.now(),
cooldown: 150,
fireForce: 0.1,
rotationAngVel: 0.03,
rotationAmt: 0.03,
rotateLeft() {
Matter.Body.rotate(this.body, -this.rotationAmt);
Matter.Body.setAngularVelocity(
this.body, -this.rotationAngVel
);
},
rotateRight() {
Matter.Body.rotate(this.body, this.rotationAmt);
Matter.Body.setAngularVelocity(
this.body, this.rotationAngVel
);
},
fire() {
if (Date.now() - this.lastShot < this.cooldown) {
return;
}
// move the bullet away from the player a bit
const {x: bx, y: by} = this.body.position;
const x = bx + (Math.cos(this.body.angle) * 10);
const y = by + (Math.sin(this.body.angle) * 10);
const bullet = Matter.Bodies.circle(
x, y, 4, {
frictionAir: 0.006,
density: 0.1,
render: {fillStyle: "yellow"},
},
);
bullets.add(bullet);
Matter.Composite.add(engine.world, bullet);
Matter.Body.applyForce(
bullet, this.body.position, {
x: Math.cos(this.body.angle) * this.fireForce,
y: Math.sin(this.body.angle) * this.fireForce,
},
);
this.lastShot = Date.now();
},
};
const bullets = new Set();
const makeEnemy = () => Matter.Bodies.polygon(
(Math.random() * (map.width - 40)) + 20,
(Math.random() * (map.height - 40)) + 20,
5, 6, {
render: {
fillStyle: "transparent",
strokeStyle: "white",
lineWidth: 1,
},
},
);
const enemies = new Set([...Array(100)].map(makeEnemy));
const walls = new Set([
Matter.Bodies.rectangle(
0, map.height / 2, 20, map.height, {isStatic: true}
),
Matter.Bodies.rectangle(
map.width / 2, 0, map.width, 20, {isStatic: true}
),
Matter.Bodies.rectangle(
map.width, map.height / 2, 20, map.height, {isStatic: true}
),
Matter.Bodies.rectangle(
map.width / 2, map.height, map.width, 20, {isStatic: true}
),
]);
Matter.Composite.add(engine.world, [
player.body, ...walls, ...enemies
]);
const keyHandlers = {
ArrowLeft: () => player.rotateLeft(),
ArrowRight: () => player.rotateRight(),
Space: () => player.fire(),
};
const validKeys = new Set(Object.keys(keyHandlers));
const keysDown = new Set();
document.addEventListener("keydown", e => {
if (validKeys.has(e.code)) {
e.preventDefault();
keysDown.add(e.code);
}
});
document.addEventListener("keyup", e => {
if (validKeys.has(e.code)) {
e.preventDefault();
keysDown.delete(e.code);
}
});
Matter.Events.on(engine, "beforeUpdate", event => {
[...keysDown].forEach(k => {
keyHandlers[k]?.();
});
if (enemies.size < 100 && Math.random() > 0.95) {
const enemy = makeEnemy();
enemies.add(enemy);
Matter.Composite.add(engine.world, enemy);
}
});
Matter.Events.on(engine, "collisionStart", event => {
for (const {bodyA, bodyB} of event.pairs) {
const [a, b] = [bodyA, bodyB].sort((a, b) =>
bullets.has(a) ? -1 : 1
);
if (bullets.has(a) && walls.has(b)) {
Matter.Composite.remove(engine.world, a);
bullets.delete(a);
}
else if (bullets.has(a) && enemies.has(b)) {
Matter.Composite.remove(engine.world, a);
Matter.Composite.remove(engine.world, b);
bullets.delete(a);
enemies.delete(b);
document.querySelector("span").textContent = ++player.score;
}
}
});
Matter.Render.run(render);
Matter.Runner.run(Matter.Runner.create(), engine);
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
<div>press left/right arrow keys to rotate and space to shoot</div>
<div>score: <span>0</span></div>

Camera not following spaceship

I'm a newb, when it comes to developing games, especially with a new language and framework. I'm having trouble at making the camera stick to the player. Not sure where should I put the this.camera.startFollow(); statement so it would work. The game is an asteroids remake, however, the map is larger than 800x600. The game is already set up with socket.io. Here's the code:
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 3200,
height: 2400,
physics: {
default: 'arcade',
arcade: {
debug: false,
gravity: {
x: 0,
y: 0
}
}
},
scene: {
preload: preload,
create: create,
update: update
}
};
const game = new Phaser.Game(config);
function preload() {
this.load.image('ship', 'assets/spaceShips_001.png');
this.load.image('otherPlayer', 'assets/enemyBlack5.png');
this.load.image('star', 'assets/star_gold.png');
this.load.image('backgroundStar1', 'assets/star2.png');
this.load.image('backgroundStar2', 'assets/star3.png');
this.load.image('space', 'assets/deep-space.jpg');
//this.load.image('background', 'assets/background.png');
}
function create() {
var self = this;
this.add.tileSprite(0, 0, 6400, 4800, 'space');
this.physics.world.setBounds(0, 0, 3200, 3200);
//this.cameras.main.setBounds(0, 0, 3200, 3200);
this.socket = io();
this.otherPlayers = this.physics.add.group();
this.socket.on('currentPlayers', function (players) {
Object.keys(players).forEach(function (id) {
if (players[id].playerId === self.socket.id) {
addPlayer(self, players[id]);
//this.camera.startFollow(self.ship); - does not work
} else {
addOtherPlayers(self, players[id]);
}
});
});
this.socket.on('newPlayer', function (playerInfo) {
addOtherPlayers(self, playerInfo);
});
this.socket.on('disconnect', function (playerId) {
self.otherPlayers.getChildren().forEach(function (otherPlayer) {
if (playerId === otherPlayer.playerId) {
otherPlayer.destroy();
}
});
});
this.socket.on('playerMoved', function (playerInfo) {
self.otherPlayers.getChildren().forEach(function (otherPlayer) {
if (playerInfo.playerId === otherPlayer.playerId) {
otherPlayer.setRotation(playerInfo.rotation);
otherPlayer.setPosition(playerInfo.x, playerInfo.y);
}
});
});
this.cursors = this.input.keyboard.createCursorKeys();
this.blueScoreText = this.add.text(16, 16, '', { fontSize: '32px', fill: '#0000FF' });
this.redScoreText = this.add.text(584, 16, '', { fontSize: '32px', fill: '#FF0000' });
this.socket.on('scoreUpdate', function (scores) {
self.blueScoreText.setText('Blue: ' + scores.blue);
self.redScoreText.setText('Red: ' + scores.red);
});
this.socket.on('starLocation', function (starLocation) {
if (self.star) self.star.destroy();
self.star = self.physics.add.image(starLocation.x, starLocation.y, 'star');
self.physics.add.overlap(self.ship, self.star, function () {
this.socket.emit('starCollected');
}, null, self);
});
}
function addPlayer(self, playerInfo) {
self.ship = self.physics.add.image(playerInfo.x, playerInfo.y, 'ship').setOrigin(0.5, 0.5).setDisplaySize(53, 40).setCollideWorldBounds(true);
if (playerInfo.team === 'blue') {
self.ship.setTint(0x0000ff);
} else {
self.ship.setTint(0xff0000);
}
self.ship.setDrag(100);
self.ship.setAngularDrag(100);
self.ship.setMaxVelocity(200);
}
function addOtherPlayers(self, playerInfo) {
const otherPlayer = self.add.sprite(playerInfo.x, playerInfo.y, 'otherPlayer').setOrigin(0.5, 0.5).setDisplaySize(53, 40);
if (playerInfo.team === 'blue') {
otherPlayer.setTint(0x0000ff);
} else {
otherPlayer.setTint(0xff0000);
}
otherPlayer.playerId = playerInfo.playerId;
self.otherPlayers.add(otherPlayer);
}
function update() {
if (this.ship) {
if (this.cursors.left.isDown) {
this.ship.setAngularVelocity(-150);
} else if (this.cursors.right.isDown) {
this.ship.setAngularVelocity(150);
} else {
this.ship.setAngularVelocity(0);
}
if (this.cursors.up.isDown) {
this.physics.velocityFromRotation(this.ship.rotation + 1.5, 100, this.ship.body.acceleration);
} else {
this.ship.setAcceleration(0);
}
this.physics.world.wrap(this.ship, 5);
// emit player movement
var x = this.ship.x;
var y = this.ship.y;
var r = this.ship.rotation;
if (this.ship.oldPosition && (x !== this.ship.oldPosition.x || y !== this.ship.oldPosition.y || r !== this.ship.oldPosition.rotation)) {
this.socket.emit('playerMovement', { x: this.ship.x, y: this.ship.y, rotation: this.ship.rotation });
}
// save old position data
this.ship.oldPosition = {
x: this.ship.x,
y: this.ship.y,
rotation: this.ship.rotation
};
}
}
Solved by adding the following code in the create() function:
window.scene = this;
then changed my commented line
//this.cameras.main.setBounds(0, 0, 3200, 3200);
to
scene.cameras.main.setBounds(0, 0, 3200, 3200);
and lastly added
scene.cameras.main.startFollow(self.ship);
in my addPlayer() function.

How to fix this? GSAP animation for Page transition work perfectly only the first time

I`m using barba.js with Gsap.
The idea is to have a transition from the home page and the about page with a centered logo animation, very simple. I click on my button, the transition comes in, the logo scale from 0 to 1, and go down fading, great!
The first animation works as it should but when I clicked again the scale factor is now missing, unfortunately.
How can I fix this to make it work? have a look at my code pen project:
https://codepen.io/cat999/project/editor/AEeEdg
Here my JS
function delay(n) {
n = n || 2000;
return new Promise((done) => {
setTimeout(() => {
done();
}, n);
});
}
function pageTransition() {
var tl = gsap.timeline();
tl.to(".loading-screen", {
duration: 1,
width: "100%",
left: "0%",
ease: "Expo.easeInOut",
});
tl.to("#svg-1", {
duration: 1, opacity: 0, y: 30, stagger: 0.4, delay: 0.2,
});
tl.to(".loading-screen", {
duration: 1,
width: "100%",
left: "100%",
ease: "Expo.easeInOut",
delay: 0.3,
});
tl.set(".loading-screen", { left: "-100%", });
tl.set("#svg-1", { opacity: 1, y: 0 });
}
function contentAnimation() {
var tl = gsap.timeline();
tl.from(".animate-this", { duration: 1, y: 30, opacity: 0, stagger: 0.4, delay: 0.2 });
}
$(function () {
barba.init({
sync: true,
transitions: [
{
async leave(data) {
const done = this.async();
pageTransition();
await delay(2000);
done();
},
async enter(data) {
contentAnimation();
},
async once(data) {
contentAnimation();
},
},
],
});
});

Control Scaling of Canvas When dragged out of chart limit

When I dragged the datapoint out of chart's ticks limit like from max x and y axis value, the canvas increase the limits too fast. How can I control this scaling speed? So that it increase with specific number defined in the config of chart.
Here is the js fiddle link.
https://jsfiddle.net/rz7pw6j0/67/
JS
(function () {
let chartInstance;
let chartElement;
function createChart(chartElement) {
const chartConfig = {
type: 'scatter',
data: {
datasets: [
{
data: [
{
x: 0,
y:0
}
],
fill: true,
showLine: true,
lineTension: 0
}
]
},
options: {
legend: {
display: false,
},
layout: {
padding: {
left: 50,
right: 50,
top: 0,
bottom: 0
}
},
title: {
display: false,
text: 'Chart.js Interactive Points',
},
scales: {
xAxes: [
{
type: 'linear',
display: true,
padding: 20,
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10,
scaleLabel: {
display: true,
labelString: 'Time',
},
ticks: {
suggestedMin: -5,
suggestedMax: 5,
stepValue: 1,
}
}
],
yAxes: [
{
type: 'linear',
display: true,
scaleLabel: {
display: true,
labelString: 'Weight'
},
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10,
ticks: {
suggestedMin: 0,
suggestedMax: 3,
stepValue: 1
},
}
]
},
responsive: true,
maintainAspectRatio: true,
tooltips: {
intersect: true,
}
}
};
chartInstance = new Chart(chartElement.getContext('2d'), chartConfig);
let element = null;
let scaleY = null;
let scaleX = null;
let datasetIndex = null;
let index = null;
let valueY = null;
let valueX = null;
function onGetElement() {
const event = d3.event.sourceEvent;
element = chartInstance.getElementAtEvent(event)[0];
if (!element) {
chartClickHandler(event);
return;
}
if (event.shiftKey) {
const tempDatasetIndex = element['_datasetIndex'];
const tempIndex = element['_index'];
chartInstance.data.datasets[tempDatasetIndex].data = chartInstance
.data.datasets[tempDatasetIndex].data.filter((v, i) => i !== tempIndex);
chartInstance.update();
return;
}
scaleY = element['_yScale'].id;
scaleX = element['_xScale'].id;
}
function onDragStart() {
const event = d3.event.sourceEvent;
datasetIndex = element['_datasetIndex'];
index = element['_index'];
valueY = chartInstance.scales[scaleY].getValueForPixel(event.offsetY);
valueX = chartInstance.scales[scaleX].getValueForPixel(event.offsetX);
chartInstance.data.datasets[datasetIndex].data[index] = {
x: valueX,
y: valueY
};
chartInstance.update(0);
}
function onDragEnd() {
if (
chartInstance.data.datasets[datasetIndex] &&
chartInstance.data.datasets[datasetIndex].data) {
chartInstance.data.datasets[datasetIndex].data.sort((a, b) => a.x - b.x > 0 ? 1 : -1);
chartInstance.update(0);
}
element = null;
scaleY = null;
scaleX = null;
datasetIndex = null;
index = null;
valueY = null;
valueX = null;
}
d3.select(chartInstance.chart.canvas).call(
d3.drag().container(chartInstance.chart.canvas)
.on('start', onGetElement)
.on('drag', onDragStart)
.on('end', onDragEnd)
);
}
function chartClickHandler (event) {
let scaleRef;
let valueX = 0;
let valueY = 0;
Object.keys(chartInstance.scales).forEach((scaleKey) => {
scaleRef = chartInstance.scales[scaleKey];
if (scaleRef.isHorizontal() && scaleKey === 'x-axis-1') {
valueX = scaleRef.getValueForPixel(event.offsetX);
} else if (scaleKey === 'y-axis-1') {
valueY = scaleRef.getValueForPixel(event.offsetY);
}
});
const newPoint = {
x: valueX,
y: valueY
};
const dataSeries = chartInstance.data.datasets[0].data;
for (let i = 0; i < dataSeries.length; i++) {
if (dataSeries[i].x === newPoint.x) {
dataSeries.y = newPoint.y;
chartInstance.update();
return;
}
}
let inserted = false;
for (let j = dataSeries.length - 1; j >= 0; j--) {
if (dataSeries[j].x > newPoint.x) {
dataSeries[j + 1] = dataSeries[j];
} else {
dataSeries[j + 1] = newPoint;
inserted = true;
break;
}
}
if (!inserted) {
dataSeries.push(newPoint);
}
chartInstance.update();
}
chartElement = document.getElementById("chart");
createChart(chartElement);
})();
HTML
<body>
<div>Chart Drag and Click Test</div>
<div class="wrapper">
<canvas id="chart"></canvas>
</div>
</body>
CSS
.wrapper {
display: "block";
}
I want to control the scaling speed.
If a dragStart event occurs beyond the scale limits, the increment should be a fixed value to avoid the issue you mentioned. Also, ticks.min and ticks.max should be set for the same purpose. Below is a sample jsfiddle and code (you can control speed by step).
https://jsfiddle.net/oLrk3fb2/
function onDragStart() {
const event = d3.event.sourceEvent;
const scales = chartInstance.scales;
const scaleInstanceY = scales[scaleY];
const scaleInstanceX = scales[scaleX];
const scalesOpts = chartInstance.options.scales;
const ticksOptsX = scalesOpts.xAxes[0].ticks;
const ticksOptsY = scalesOpts.yAxes[0].ticks;
const step = 1;
datasetIndex = element['_datasetIndex'];
index = element['_index'];
valueY = scaleInstanceY.getValueForPixel(event.offsetY);
valueX = scaleInstanceX.getValueForPixel(event.offsetX);
if (valueY < scaleInstanceY.min) {
ticksOptsY.min = valueY = scaleInstanceY.min - step;
}
if (valueY > scaleInstanceY.max) {
ticksOptsY.max = valueY = scaleInstanceY.max + step;
}
if (valueX < scaleInstanceX.min) {
ticksOptsX.min = valueX = scaleInstanceX.min - step;
}
if (valueX > scaleInstanceX.max) {
ticksOptsX.max = valueX = scaleInstanceX.max + step;
}
chartInstance.data.datasets[datasetIndex].data[index] = {
x: valueX,
y: valueY
}
chartInstance.update(0);
}

Categories

Resources