Javascript not connected to CDN link? - javascript

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>

Related

how to leave only those days that are specified in JSON?

Actual behaviour Now a series of all days of the week is being built on the X-axis. like now: 2022-5-30 2022-5-31 2022-6-1 2022-6-2 2022-6-3 2022-6-4 2022-6-5 2022-6-6
Live demo with steps to reproduce
https://jsfiddle.net/5z1b9xnp/
let globalData = []; let finRez = [];
let chart;
let duration = 500; // Determines how long the animation between new points should be take
let startIterator = 1; // Determines how many points will be rendered on chart's init
let currentIterator = startIterator;
let maxIterator = 1;
let guiButton = document.getElementById('start');
let guiButtonState = 'Start';
let intervalId;
// Fetch data:
fetch('https://raw.githubusercontent.com/veleg/trade/main/trade.json')
.then(response => response.json())
.then(data => {
parseData(data);
createChart();
initEvents();
});
function initEvents() {
guiButton.addEventListener('click', function() {
if (guiButtonState === 'Stop') {
// User clicked "Stop" -> stop animation and allow to resume
intervalId = clearInterval(intervalId);
guiButton.innerText = guiButtonState = 'Resume';
} else {
// If animation has finished, recreate chart
if (guiButtonState === 'Restart') {
createChart();
}
guiButton.innerText = guiButtonState = 'Stop';
// Start animation:
redrawChart(currentIterator += 1);
intervalId = setInterval(function() {
// If we reached last available point, stop animation:
if (currentIterator === maxIterator) {
intervalId = clearInterval(intervalId);
currentIterator = startIterator;
guiButton.innerText = guiButtonState = 'Restart';
} else {
redrawChart(currentIterator += 1);
}
}, duration);
}
});
}
function redrawChart(index) {
// Set new subtitle on every redraw
chart.setTitle(null, {
text: ' Заработано: ' + globalData[0].data[index][1]
}, false);
const newValues = globalData.map(series => series.data[index][1]);
const maxIndex = newValues.indexOf(Math.max.apply(null, newValues));
// To each series, add a point:
chart.series.forEach(
(series, seriesIndex) => {
const enabled = maxIndex === seriesIndex && ((index < 5) || (index % 5 === 0));
series.addPoint(
{
x: globalData[seriesIndex].data[index][0],
y: globalData[seriesIndex].data[index][1]
<!-- dataLabels: { -->
<!-- enabled -->
<!-- }, -->
<!-- marker: { -->
<!-- enabled -->
<!-- } -->
},
false,
false,
false
);
}
);
// Now, once everything is updated, redraw chart:
chart.redraw({
duration
});
}
function parseData(data) {
Highcharts.objectEach(
data,
// Prepare Highcharts data-format:
// series: [{
// data: [ [x, y], [x, y], ..., [x, y]]
// }]
(countryData, country) => {
if (country !== 'Diamond Princess' && country !== 'Holy See') {
globalData.push({
name: country,
data: countryData.map(p => [Date.parse(p.date), p.recovered / getCountryPopulation(country)])
<!-- data: countryData.map(p => [p.date, p.recovered / getCountryPopulation(country)]), -->
<!-- datas: countryData.date, -->
<!-- datass: countryData.map(p => [p.date, p.date]) -->
});
}
}
);
// Sort and limit dataset:
globalData = globalData
.sort((countryA, countryB) => {
let countryALen,
countryBLen;
if (!countryA || !countryA.data || countryA.data.length === 0) {
return 1;
}
if (!countryB || !countryB.data || countryB.data.length === 0) {
return -1;
}
return countryB.data[countryB.data.length - 1][1] - countryA.data[countryA.data.length - 1][1];
})
.splice(0, 8);
maxIterator = Math.max.apply(null, globalData.map(series => series.data.length - 1));
}
function createChart() {
function format(y) {
return y.toFixed(2);
}
chart = Highcharts.chart('container', {
chart: {
type: 'spline',
marginLeft: 100
},
legend: {
layout: 'proximate',
align: 'right'
},
title: {
floating: true,
align: 'left',
x: 93,
y: 20,
text: 'Confirmed cases per country per 1000 inhabitants'
},
subtitle: {
floating: true,
align: 'left',
y: 60,
x: 90,
text: ' Заработано: ' + globalData[0].data[0][1],
style: {
fontSize: '20px'
}
},
tooltip: {
split: true,
pointFormatter: function() {
<!-- var value = format(this.y); -->
<!-- return `<span style="color:${this.color}">●</span> ${this.series.name}: <b>${value}</b><br/>`; -->
}
},
yAxis: {
title: {
text: ''
},
maxPadding: 0.2,
softMax: 1
},
xAxis: {
gridLineWidth: 2,
min: globalData[0].data[0][0],
minRange: 7 * 24 * 3600 * 1000,
type: 'datetime'
<!-- categories: [1] -->
},
plotOptions: {
series: {
animation: {
duration
},
<!-- marker: { -->
<!-- symbol: 'circle' -->
<!-- }, -->
dataLabels: {
formatter: function() {
return format(this.y);
}
}
}
},
series: globalData.map(series => {
return {
name: series.name,
data: series.data.slice(0, startIterator).map(point => {
return { x: point[0], y: point[1] }
})
}
})
});
}
function getCountryPopulation(country) {
return {
"Заработано": 1,
"Финансовый результат": 1
}[country];
}
Product version Highcharts JS v10.3.2 (2022-11-28)
It is necessary to skip days on the X axis that are not in the JSON file. need: 2022-5-30 2022-5-31 2022-6-1 2022-6-2 2022-6-3 2022-6-6
I don't need to filter JSON. The JSON file just indicates what I need, but the script itself substitutes dates that are not in JSON.
Be careful, dates are skipped in JSON, and the library inserts missing dates. I need only those dates that are specified in JSON, it can be weekends or two days a week.

How do you apply Smart Routing on links with ports on JointJS?

I am trying to apply smart routing of links with the use of ports using JointJS. This documentation shows the one I am trying to achieve. The example on the docs though shows only the programmatic way of adding Link from point A to point B. How do you do this with the use of ports?
Here's my code: JSFiddle.
HTML:
<html>
<body>
<button id="btnAdd">Add Table</button>
<div id="dbLookupCanvas"></div>
</body>
</html>
JS
$(document).ready(function() {
$('#btnAdd').on('click', function() {
AddTable();
});
InitializeCanvas();
// Adding of two sample tables on first load
AddTable(50, 50);
AddTable(250, 50);
});
var graph;
var paper
var selectedElement;
var namespace;
function InitializeCanvas() {
let canvasContainer = $('#dbLookupCanvas').parent();
namespace = joint.shapes;
graph = new joint.dia.Graph({}, {
cellNamespace: namespace
});
paper = new joint.dia.Paper({
el: document.getElementById('dbLookupCanvas'),
model: graph,
width: canvasContainer.width(),
height: 500,
gridSize: 10,
drawGrid: true,
cellViewNamespace: namespace,
validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
return (magnetS !== magnetT);
},
snapLinks: {
radius: 20
}
});
//Dragging navigation on canvas
var dragStartPosition;
paper.on('blank:pointerdown',
function(event, x, y) {
dragStartPosition = {
x: x,
y: y
};
}
);
paper.on('cell:pointerup blank:pointerup', function(cellView, x, y) {
dragStartPosition = null;
});
$("#dbLookupCanvas")
.mousemove(function(event) {
if (dragStartPosition)
paper.translate(
event.offsetX - dragStartPosition.x,
event.offsetY - dragStartPosition.y);
});
// Remove links not connected to anything
paper.model.on('batch:stop', function() {
var links = paper.model.getLinks();
_.each(links, function(link) {
var source = link.get('source');
var target = link.get('target');
if (source.id === undefined || target.id === undefined) {
link.remove();
}
});
});
paper.on('cell:pointerdown', function(elementView) {
resetAll(this);
let isElement = elementView.model.isElement();
if (isElement) {
var currentElement = elementView.model;
currentElement.attr('body/stroke', 'orange');
selectedElement = elementView.model;
} else
selectedElement = null;
});
paper.on('blank:pointerdown', function(elementView) {
resetAll(this);
});
$('#dbLookupCanvas')
.attr('tabindex', 0)
.on('mouseover', function() {
this.focus();
})
.on('keydown', function(e) {
if (e.keyCode == 46)
if (selectedElement) selectedElement.remove();
});
}
function AddTable(xCoord = undefined, yCoord = undefined) {
// This is a sample database data here
let data = [
{columnName: "radomData1"},
{columnName: "radomData2"}
];
if (xCoord == undefined && yCoord == undefined)
{
xCoord = 50;
yCoord = 50;
}
const rect = new joint.shapes.standard.Rectangle({
position: {
x: xCoord,
y: yCoord
},
size: {
width: 150,
height: 200
},
ports: {
groups: {
'a': {},
'b': {}
}
}
});
$.each(data, (i, v) => {
const port = {
group: 'a',
args: {}, // Extra arguments for the port layout function, see `layout.Port` section
label: {
position: {
name: 'right',
args: {
y: 6
} // Extra arguments for the label layout function, see `layout.PortLabel` section
},
markup: [{
tagName: 'text',
selector: 'label'
}]
},
attrs: {
body: {
magnet: true,
width: 16,
height: 16,
x: -8,
y: -4,
stroke: 'red',
fill: 'gray'
},
label: {
text: v.columnName,
fill: 'black'
}
},
markup: [{
tagName: 'rect',
selector: 'body'
}]
};
rect.addPort(port);
});
rect.resize(150, data.length * 40);
graph.addCell(rect);
}
function resetAll(paper) {
paper.drawBackground({
color: 'white'
});
var elements = paper.model.getElements();
for (var i = 0, ii = elements.length; i < ii; i++) {
var currentElement = elements[i];
currentElement.attr('body/stroke', 'black');
}
var links = paper.model.getLinks();
for (var j = 0, jj = links.length; j < jj; j++) {
var currentLink = links[j];
currentLink.attr('line/stroke', 'black');
currentLink.label(0, {
attrs: {
body: {
stroke: 'black'
}
}
});
}
}
Any help would be appreciated. Thanks!
The default link created when you draw a link from a port is joint.dia.Link.
To change this you can use the defaultLink paper option, and configure the router you would like.
defaultLink documentation reference
const paper = new joint.dia.Paper({
el: document.getElementById('dbLookupCanvas'),
model: graph,
width: canvasContainer.width(),
height: 500,
gridSize: 10,
drawGrid: true,
cellViewNamespace: namespace,
validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
return (magnetS !== magnetT);
},
snapLinks: {
radius: 20
},
defaultLink: () => new joint.shapes.standard.Link({
router: { name: 'manhattan' },
connector: { name: 'rounded' },
})
});
You could also provide several default options in the paper.
defaultLink: () => new joint.shapes.standard.Link(),
defaultRouter: { name: 'manhattan' },
defaultConnector: { name: 'rounded' }

Changing Transition Timing of a CodePen

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.

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>

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