Popstate won't work with page transition animations - ajax

I'm using Fetch api with anime.js, the problem is in back and forward buttons, i can't include animations and properly remove old container and add new one...
function fetchPage(link, page) {
let baseURL = ${window.location.protocol}//${window.location.hostname};
if (window.location.port) {
baseURL += `:${window.location.port}`;
}
fetch(`${baseURL}/${page}`)
.then(function(response) {
return response.text()
})
.then(function(html) {
let doc = new DOMParser().parseFromString(html, "text/html");
anime({
targets: '.big-text, .big-text__title',
translateX: 500,
opacity: [1,0],
easing: 'easeInExpo',
duration: 500,
complete: (anim) => {
document.querySelector('.column-wrapper').remove();
}
})
setTimeout(function () {
document.querySelector('body').insertBefore(doc.querySelector('.new-content'),null);
$.getScript('js/separated.js');
anime({
targets: '.big-text, .big-text__title',
translateX: [-500, 0],
delay: (el, i) => 100 * i,
opacity: [0, 1],
easing: 'easeOutExpo',
})
}, 700);
})
window.addEventListener('popstate', function(){
fetchPage(baseURL, location.pathname);
})
}

Related

How can I show the distance a polyline has covered while animating it in Mapbox GL JS?

I am using Mapbox GL JS to capture frame by frame video of the animation of a geoJson (similar to what is described here: https://docs.mapbox.com/mapbox-gl-js/example/animate-a-line/).
The strategy for encoding mapbox animations into mp4 here are described here:
https://github.com/mapbox/mapbox-gl-js/issues/5297 and https://github.com/mapbox/mapbox-gl-js/pull/10172 .
I would like to show the distance the polyline has covered as I draw each frame. I need the distance to be in the GL itself (as opposed to, for example, an HTML element on top of the canvas), since that's where I'm capturing the video from.
Can someone help describe a performant strategy for doing this to me?
Aside from tearing apart the Mapbox GL JS, why not simply try drawing directly onto the mapbox canvas?
In this example, there are two canvases, created identically.
With the second, there is another requestAnimationFrame loop that adds an overlay.
I've also shown how it can be recorded with MediaRecorder.
const canvas1 = document.getElementById('canvas1')
const canvas2 = document.getElementById('canvas2')
const video = document.getElementById('output')
const status = document.getElementById('status')
let x = 0 // Value to be displayed
const setupCanvas = (canvas) => {
canvas.height = 300
canvas.width = 300
const canvas1Ctx = canvas.getContext('2d')
canvas1Ctx.fillStyle = 'black'
canvas1Ctx.fillRect(300, 100, 100, 100)
const animateCanvas = () => {
x += 2;
if (x > 300) x = 10
canvas1Ctx.fillStyle = 'rgba(20,20,20,1)'
canvas1Ctx.fillRect(0, 0, canvas.width, canvas.height)
canvas1Ctx.beginPath()
canvas1Ctx.arc(x, 100, 20, 0, 2 * Math.PI)
canvas1Ctx.fillStyle = 'rgba(250,0,0,1)'
canvas1Ctx.fill()
requestAnimationFrame(animateCanvas)
}
animateCanvas()
}
const addOverlay = (canvas) => {
const canvasCtx = canvas2.getContext('2d')
function animateOverlay() {
canvasCtx.font = '48px serif'
canvasCtx.fillStyle = 'white'
canvasCtx.fillText(`X: ${x}`, 10, 50)
requestAnimationFrame(animateOverlay)
}
animateOverlay()
}
const initVideoCapture = () => {
var videoStream = canvas2.captureStream(30)
var mediaRecorder = new MediaRecorder(videoStream)
var chunks = []
mediaRecorder.ondataavailable = function(e) {
chunks.push(e.data)
}
mediaRecorder.onstop = function(e) {
var blob = new Blob(chunks, {
'type': 'video/mp4'
})
chunks = []
var videoURL = URL.createObjectURL(blob)
video.src = videoURL
}
mediaRecorder.ondataavailable = function(e) {
chunks.push(e.data)
}
mediaRecorder.start()
status.textContent = 'Recording...'
setTimeout(function() {
mediaRecorder.stop()
status.textContent = 'Complete'
}, 5000)
}
setupCanvas(canvas1)
setupCanvas(canvas2)
addOverlay(canvas2)
initVideoCapture()
<canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas>
<video id="output" autoplay controls></video>
<p>Status: <span id="status">Loading</span></p>
Try this :
// show the distance a polyline has covered while animating it in mapbox gl js
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-74.5, 40],
zoom: 9,
})
var geojson = {
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: [
[-74.5, 40],
[-74.45, 40.7],
[-74.36, 40.8],
[-74.36, 41.2],
],
},
}
map.on('load', function () {
map.addLayer({
id: 'route',
type: 'line',
source: {
type: 'geojson',
data: geojson,
},
layout: {
'line-join': 'round',
'line-cap': 'round',
},
paint: {
'line-color': '#888',
'line-width': 8,
},
})
var lineDistance = turf.lineDistance(geojson, 'kilometers')
var arc = []
var maxTravelTime = 0
for (var i = 0; i < lineDistance; i += lineDistance / 200) {
var segment = turf.along(geojson, i, 'kilometers')
arc.push(segment.geometry.coordinates)
}
map.addLayer({
id: 'point',
type: 'symbol',
source: {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: arc[0],
},
},
],
},
},
layout: {
'icon-image': 'car',
'icon-rotate': ['get', 'bearing'],
'icon-rotation-alignment': 'map',
'icon-allow-overlap': true,
'icon-ignore-placement': true,
},
})
function animate() {
map.getSource('point').setData(geojson)
if (maxTravelTime < lineDistance) {
maxTravelTime += lineDistance / 200
} else {
maxTravelTime = 0
}
requestAnimationFrame(animate)
}
animate()
})

How to detect click on chart js 3.7.1 axis label?

How to detect click on an axis label with chart.js
In the example bellow, I can only detect click on the graph itself
https://stackblitz.com/edit/ng2-charts-bar-template-qchyz6
You will need to implement a custom plugin that can listen to all the events of the canvas:
const findLabel = (labels, evt) => {
let found = false;
let res = null;
labels.forEach(l => {
l.labels.forEach((label, index) => {
if (evt.x > label.x && evt.x < label.x2 && evt.y > label.y && evt.y < label.y2) {
res = {
label: label.label,
index
};
found = true;
}
});
});
return [found, res];
};
const getLabelHitboxes = (scales) => (Object.values(scales).map((s) => ({
scaleId: s.id,
labels: s._labelItems.map((e, i) => ({
x: e.translation[0] - s._labelSizes.widths[i],
x2: e.translation[0] + s._labelSizes.widths[i] / 2,
y: e.translation[1] - s._labelSizes.heights[i] / 2,
y2: e.translation[1] + s._labelSizes.heights[i] / 2,
label: e.label,
index: i
}))
})));
const plugin = {
id: 'customHover',
afterEvent: (chart, event, opts) => {
const evt = event.event;
if (evt.type !== 'click') {
return;
}
const [found, labelInfo] = findLabel(getLabelHitboxes(chart.scales), evt);
if (found) {
console.log(labelInfo);
}
}
}
Chart.register(plugin);
const options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink'
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderColor: 'orange'
}
]
},
options: {}
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.js"></script>
</body>
Adpatation for Angular with ng2-charts (chart-js v3.7.1)
Just use Chart.register
i.e. put the following functiion into component ngOnInit()
RegisterPlugin() {
Chart.register(
{
id: 'yAxisCustomClick',
afterEvent: (chart: Chart<'bar'>, event: {
event: ChartEvent;
replay: boolean;
changed?: boolean | undefined;
cancelable: false;
inChartArea: boolean
}) => {
const evt = event.event;
if (evt.type === 'click' && evt.x! < Object.values(chart.scales).filter(s => s.id === 'x')[0].getBasePixel()) {
const labelIndex = Object.values(chart.scales).filter(s => s.id === 'y')[0].getValueForPixel(evt.y!);
const label = Object.values(chart.scales).filter(s => s.id === 'y')[0].getTicks()[labelIndex!]?.label;
if (label) {
console.log('Do the stuff for', label)
}
}
}
}
);
}
Example in stackblitz udpated
https://stackblitz.com/edit/ng2-charts-bar-template-qchyz6

Odd animation with multiple series

I was trying to adapt the spline animation for time series (https://www.highcharts.com/demo/dynamic-update) for multiple series.
I modified the example here https://jsfiddle.net/2wj3fquL/
Highcharts.chart('container', {
chart: {
type: 'spline',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: function () {
// set up the updating of the chart each second
var series = this.series;
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = Math.random();
series[0].addPoint([x, y], true, true);
y = Math.random();
series[1].addPoint([x, y], true, true);
}, 1000);
}
}
},
time: {
useUTC: false
},
title: {
text: 'Live random data'
},
accessibility: {
announceNewData: {
enabled: true,
minAnnounceInterval: 15000,
announcementFormatter: function (allSeries, newSeries, newPoint) {
if (newPoint) {
return 'New point added. Value: ' + newPoint.y;
}
return false;
}
}
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
headerFormat: '<b>{series.name}</b><br/>',
pointFormat: '{point.x:%Y-%m-%d %H:%M:%S}<br/>{point.y:.2f}'
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
name: 'Random data',
data: (function () {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
for (i = -19; i <= 0; i += 1) {
data.push({
x: time + i * 1000,
y: Math.random()
});
}
return data;
}())
}, {
name: 'other data',
data: (function () {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
for (i = -19; i <= 0; i += 1) {
data.push({
x: time + i * 1000,
y: Math.random()
});
}
return data;
}())
}]
});
Unfortunately the effect is odd since one series moves smooth while the other doesn't ...
How could I solve this problem?
Thanks
That is because the chart is redrawn twice. Disable redraw in the first addPoint method call.
events: {
load: function() {
...
setInterval(function() {
...
series[0].addPoint([x, y], false, true);
series[1].addPoint([x, y], true, true);
}, 1000);
}
}
Live demo: https://jsfiddle.net/BlackLabel/0n2yw57m/
API Reference: https://api.highcharts.com/class-reference/Highcharts.Series#addPoint

Phaser 3.17 doesn't setect collision between tilemap layer and player sprite

No collisions between map layer and player sprite. But collisions between world bounds work. What is wrong?
I tried different workarounds that could find online and none of them worked.
Game config
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: "#f",
physics: {
default: 'arcade',
arcade: {
gravity: { y: gameState.gravity },
debug: true
}
},
scene: {
preload,
create,
update
}
};
Code in create() regarding the tilemap and its layers and the character
gameState.map = this.add.tilemap('map');
gameState.dungeonTileset = gameState.map.addTilesetImage('dungeon', 'dungeonTiles');
gameState.backgroundLayer = gameState.map.createStaticLayer('Background', gameState.dungeonTileset);
gameState.mapLayer = gameState.map.createStaticLayer('Map', gameState.dungeonTileset);
gameState.miscLayer = gameState.map.createStaticLayer('Misc', gameState.dungeonTileset);
gameState.mapLayer.setCollisionByExclusion([-1]);
this.physics.world.bounds.width = gameState.mapLayer.width;
this.physics.world.bounds.height = gameState.mapLayer.height;
gameState.player = this.physics.add.sprite(73, 398, 'player', 0);
gameState.player.setCollideWorldBounds(true);
this.physics.add.collider(gameState.player, gameState.mapLayer);
No warning and no errors are coming up in the console. I don't know what to do anymore.
Thanks in advance!
I have addited a little bit the original example so you can see how it looks, I think the problem in your code is the line concerning gameState.mapLayer.setCollisionByExclusion([-1]); but am not sure because I can't see how the tilemap is created
var config = {
type: Phaser.WEBGL,
width: 800,
height: 576,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
loader: {
baseURL: 'https://labs.phaser.io',
crossOrigin: 'anonymous'
},
pixelArt: true,
physics: {
default: 'arcade',
arcade: { gravity: { y: 300 } }
},
scene: {
preload: preload,
create: create,
update: update
}
};
var game = new Phaser.Game(config);
var map;
var cursors;
var player;
var groundLayer;
function preload ()
{
this.load.image('ground_1x1', 'assets/tilemaps/tiles/ground_1x1.png');
this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/tile-collision-test.json');
this.load.image('player', 'assets/sprites/phaser-dude.png');
}
function create ()
{
map = this.make.tilemap({ key: 'map' });
var groundTiles = map.addTilesetImage('ground_1x1');
map.createDynamicLayer('Background Layer', groundTiles, 0, 0);
groundLayer = map.createDynamicLayer('Ground Layer', groundTiles, 0, 0);
groundLayer.setCollisionBetween(1, 25);//here
player = this.physics.add.sprite(80, 70, 'player')
.setBounce(0.1);
this.physics.add.collider(player, groundLayer);
cursors = this.input.keyboard.createCursorKeys();
this.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.cameras.main.startFollow(player);
}
function update ()
{
player.body.setVelocityX(0);
if (cursors.left.isDown)
{
player.body.setVelocityX(-200);
}
else if (cursors.right.isDown)
{
player.body.setVelocityX(200);
}
if (cursors.up.isDown && player.body.onFloor())
{
player.body.setVelocityY(-300);
}
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.17.0/dist/phaser.min.js"></script>

dc.js clicking on one chart not filtering the other (different dimensions)

I've been happily using DC.js for some time. Today is the first time I've created two charts, from two dimensions, both running off the same crossfiltered variable, facts - and they don't filter each other. The bubble chart filters the bar chart but not vice versa. Am I making some obvious error? Very grateful for any pointers.
var facts = crossfilter(data);
var all = facts.groupAll();
print_filter(facts);
var appDimension = facts.dimension(function(d){ return d.ShortName; });
var appGroup = appDimension.group().reduce(
function(p,v){ p.count++; v.NumUsers==0?p.numUsers=1:p.numUsers=v.NumUsers; p.numClients=v.NumClients; p.lc=v.lc; p.LifeCycle=v.LifeCycle; p.fv=v.fv; p.FutureValue=v.FutureValue; return p; },
function(p,v){ p.count--; v.NumUsers==0?p.numUsers=1:p.numUsers=v.NumUsers; p.numClients=v.NumClients; p.lc=v.lc; p.LifeCycle=v.LifeCycle; p.fv=v.fv; p.FutureValue=v.FutureValue; return p; },
function(){ return { count:0, numUsers: 0, numClients: 0, lc: 0, LifeCycle: '', fv: 0, FutureValue: '' }; }
);
var lifeCycleDimension = facts.dimension(function(d){ return d.LifeCycle; });
var tempName='';
var lifeCycleGroup = lifeCycleDimension.group().reduce(
function(p,v){
if (tempName!=v.ShortName) {
p++;
tempName=v.ShortName;
}
return p;
},
function(p,v){
if (tempName!=v.ShortName) {
p--;
tempName=v.ShortName;
}
return p;
},
function(){ return 0; }
);
var yearlyBubbleChart = dc.bubbleChart('#col1')
.width(360)
.height(600)
.margins({top: 10, right: 100, bottom: 30, left: 40})
.dimension(appDimension)
.group(appGroup)
.clipPadding(200)
.yAxisLabel('Number of users')
.xAxisLabel('Number of clients')
// .ordinalColors(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628'])
.colorAccessor(function(p){
return p.value.lc;
})
.colors(colorbrewer.RdYlGn[9])
.colorDomain([1,6])
.keyAccessor(function (p) {
return p.value.numClients;
})
.valueAccessor(function (p) {
return p.value.numUsers;
})
.radiusValueAccessor(function (p) {
return p.value.fv;
})
.title(function(d){ return 'App: '+d.key + '\nNum users: '+d.value.numUsers+'\nNum clients: '+d.value.numClients+'\nLife cycle: '+d.value.LifeCycle+'\nFuture value: '+d.value.FutureValue; })
.maxBubbleRelativeSize(0.15)
.x(d3.scale.linear().domain([0, 608]))
.y(d3.scale.log().base(Math.E).domain([1, 120000]))
.r(d3.scale.linear().domain([0.5, 3]))
// .elasticY(true)
// .elasticX(true)
.yAxisPadding(100)
.xAxisPadding(500)
.renderHorizontalGridLines(true)
.renderVerticalGridLines(true);
yearlyBubbleChart.yAxis().tickFormat(function(d){ return Math.round(d); });
yearlyBubbleChart.xAxis().ticks(5);
var barAmountChart = dc.barChart('#barCount')
.width(530)
.height(200)
.margins({top: 10, right: 50, bottom: 30, left: 45})
.dimension(lifeCycleDimension)
.gap(1)
.group(lifeCycleGroup)
.title(function(d){ return d.value+' apps in '+d.key+' phase '; })
.x(d3.scale.ordinal().domain(['Idea','Plan 2017','New','Re-Newed','SunSet','Legacy']))
.xUnits(dc.units.ordinal);
barAmountChart.yAxis().ticks(4);

Resources