Infinite draggable webgl slider - slider

I'm new to webgl and I'm trying to imrpove this infinite draggable webgl slider:
https://codepen.io/ReGGae/pen/povjKxV
setup() {
const { ww } = store
const state = this.state
const { items, titles } = this.ui
const {
width: wrapWidth,
left: wrapDiff
} = this.el.getBoundingClientRect()
// Set bounding
state.max = -(items[items.length - 1].getBoundingClientRect().right - wrapWidth - wrapDiff)
state.min = 0
// Global timeline
this.tl = gsap.timeline({
paused: true,
defaults: {
duration: 1,
ease: 'linear'
}
})
.fromTo('.js-progress-line-2', {
scaleX: 1
}, {
scaleX: 0,
duration: 0.5,
ease: 'power3'
}, 0)
.fromTo('.js-titles', {
yPercent: 0
}, {
yPercent: -(100 - (100 / titles.length)),
}, 0)
.fromTo('.js-progress-line', {
scaleX: 0
}, {
scaleX: 1
}, 0)
// Cache stuff
for (let i = 0; i < items.length; i++) {
const el = items[i]
const { left, right, width } = el.getBoundingClientRect()
// Create webgl plane
const plane = new Plane()
plane.init(el)
// Timeline that plays when visible
const tl = gsap.timeline({ paused: true })
.fromTo(plane.mat.uniforms.uScale, {
value: 0.65
}, {
value: 1,
duration: 1,
ease: 'linear'
})
// Push to cache
this.items.push({
el, plane,
left, right, width,
min: left < ww ? (ww * 0.775) : -(ww * 0.225 - wrapWidth * 0.2),
max: left > ww ? state.max - (ww * 0.775) : state.max + (ww * 0.225 - wrapWidth * 0.2),
tl,
out: false
})
}
}
What I would like to be able to do is to perfectly loop the words with the slides. Now when dragging it fast the words don't match the pictures (currently there is one more word than the images as workaround).
Plus I would like to find a way to place links on the draggable images.
I would be very grateful for any advice.

Related

use Swiper.js to do a circle carousel

It's my code
https://codepen.io/daylilystudio/pen/XWzVvVb
I make a circle carousel like Ferris wheel,
but after every 5 carousel,a group will fade in from right.
I don't want the group fade in suddenly.
How can I just fade in one slide every time?
var certifySwiper = new Swiper('#certify .swiper-container', {
watchSlidesProgress: true,
slidesPerView: 'auto',
centeredSlides: true,
loop: true,
autoplay: true,
loopedSlides: 5,
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
pagination: {
el: '.swiper-pagination',
//clickable :true,
},
on: {
progress: function(progress) {
for (i = 0; i < this.slides.length; i++) {
var slide = this.slides.eq(i);
var slideProgress = this.slides[i].progress;
modify = 1;
if (Math.abs(slideProgress) > 1) {
modify = (Math.abs(slideProgress) - 1) * 0.3 + 1;
}
translate = slideProgress * modify * 260 + "px";
scale = 1 - Math.abs(slideProgress) / 5;
zIndex = 999 - Math.abs(Math.round(10 * slideProgress));
slide.transform("translateX(" + translate + ") scale(" + scale + ")");
slide.css("zIndex", zIndex);
slide.css("opacity", 1);
if (Math.abs(slideProgress) > 3) {
slide.css("opacity", 0);
}
}
},
setTransition: function(transition) {
for (var i = 0; i < this.slides.length; i++) {
var slide = this.slides.eq(i);
slide.transition(transition);
}
}
}
})

d3.js | projection.rotate() return value [NaN, 0, 0]

I want to rotate the globe canvas using projection.rotate() but it return value [NaN, 0, 0].
<template lang="html">
<div class="map" style="width:100vw;height:100vh">
<canvas id="globe"></canvas>
</div>
</template>
<script lang="js">
import * as d3 from "d3";
import * as topojson from "topojson-client";
export default {
name: 'vue',
data() {
return {
angles: { x: -20, y: 40, z: 0 },
canvas: null,
context: null,
countries:null,
countryList:null,
autorotate:null,
lastTime : d3.now(),
now:null,
diff:null,
roation:null,
scaleFactor: 0.6,
degPerSec : 6,
degPerMs: this.degPerSec / 1000,
width:null,
height:null,
water : {type: 'Sphere'},
colorWater : '#0099cf',
colorLand : '#77fc2f',
colorGraticule : '#0099cf',
colorCountry : '#77fc2f',
land:null,
currentCountry:null,
graticule: d3.geoGraticule10(),
projection: d3.geoOrthographic().precision(0.1),
path : null,
}
},
mounted() {
this.canvas = document.getElementById('globe');
this.context = this.canvas.getContext('2d');
this.path = d3.geoPath(this.projection).context(this.context);
this.loadData((world, cList)=> {
this.land = topojson.feature(world, world.objects.land);
this.countries = topojson.feature(world, world.objects.countries);
this.countryList = cList;
window.addEventListener('resize', this.scale);
this.scale();
this.autorotate = d3.timer(this.rotate);
});
},
methods: {
loadData(cb) {
d3.json('https://unpkg.com/world-atlas#1/world/110m.json').then(function(world) {
d3.tsv('https://gist.githubusercontent.com/mbostock/4090846/raw/07e73f3c2d21558489604a0bc434b3a5cf41a867/world-country-names.tsv').then( function(countries) {
cb(world, countries)
})
})
},
rotate(elapsed) {
this.now = d3.now();
this.diff = this.now - this.lastTime;
if (elapsed < 300) { //this.diff !== elapsed
let rotation = this.projection.rotate();
console.log(rotation);
rotation[0] += this.diff * this.degPerMs;
this.projection.rotate(rotation);
this.render();
}
this.lastTime = this.now;
},
render() {
this.context.clearRect(0, 0, this.width, this.height);
this.fill(this.water, this.colorWater)
this.stroke(this.graticule, this.colorGraticule)
this.fill(this.land, this.colorLand)
if (this.currentCountry) {
this.fill(this.currentCountry, this.colorCountry)
}
},
fill(obj, color) {
this.context.beginPath()
this.path(obj)
this.context.fillStyle = color
this.context.fill()
},
stroke(obj, color) {
this.context.beginPath()
this.path(obj)
this.context.strokeStyle = color
this.context.stroke()
},
scale() {
this.width = document.querySelector(".map").clientWidth;
this.height = document.querySelector(".map").clientHeight;
this.canvas.setAttribute('width', this.width);
this.canvas.setAttribute('height', this.height);
this.projection
.scale((this.scaleFactor * Math.min(this.width, this.height)) / 2)
.translate([this.width / 2, this.height / 2])
this.render();
}
},
}
</script>
you can check my issue here: https://codesandbox.io/s/globe-issus-q2s1s
example rotate demo: https://codepen.io/Share2U/pen/dyWQoGv
Thank you for your help.
The issue lies in the following line:
degPerMs: this.degPerSec / 1000,
this.degPerSec is not available because you can't access other data properties inside data. Therefore rotation[0] += this.diff * this.degPerMs; is returning NaN.
To fix this you can set a variable before exporting the module.
E.g.:
<script lang="js">
import * as d3 from "d3";
import * as topojson from "topojson-client";
const DEG_PER_SEC = 6;
export default {
name: 'vue',
data() {
return {
...
degPerMs: DEG_PER_SEC / 1000,
...
}
}
}
</script>

Adding markerclustergroup to leaflet overlay does not update in Vue.js app

I have an overlay control on my leaflet map in a vue.js application. It contains two layers. The ILZipCodes layer renders perfectly. However, the "markers" layer does not appear when I select the checkbox in the layer control. Why is this? I reckon I may be setting up the layer control and populating the clusters in the wrong sequence.
Cheers
<template>
<div>
<div class="mx-auto my-6 loader" v-if="isSearching"></div>
<div class="tooManyResultsError" v-if="tooManyResults">
Your search brought back too many results to display. Please zoom in or refine your search with the text box and
filters.
</div>
<div id="leafletMapId"></div>
</div>
</template>
<script>
//The center coordinate and zoom level that the map should initialize to
// to capture all of the continental United States
const INITIAL_CENTER_LATITUDE = 34;
const INITIAL_CENTER_LONGITUDE = -96;
const INITIAL_ZOOM = 4;
/* Leaflet performs slowly with reactivity ("observable" data object variables).
* To work around this, I removed myMap and markers from the data object.
*/
/* If the user zooms out or pans beyond the boundaries of the previous fetch's dataset, then
* the client fetches location data from the server to replace the previous map data. If the user
* zooms or pans but stays within the boundaries of the dataset currently displayed on the map, then
* the client does not run another fetch.
* */
import axios from "axios";
import store from "../store.js";
import L from "leaflet";
import "leaflet/dist/leaflet.css";
import "leaflet.markercluster/dist/MarkerCluster.css";
import "leaflet.markercluster/dist/MarkerCluster.Default.css";
import "leaflet.markercluster/dist/leaflet.markercluster-src.js";
import "leaflet-ajax/dist/leaflet.ajax.js";
import icon from "leaflet/dist/images/marker-icon.png";
import iconShadow from "leaflet/dist/images/marker-shadow.png";
export default {
name: "ContactsMap",
mounted() {
this.fetchAggregatedDistrictStats();
},
methods: {
async fetchAggregatedDistrictStats() {
axios.get(... // fetches some statistics for the other overlay layers
);
},
createMapWithLeafletAndMapTiler() {
var $this = this;
var streetTilesLayer = L.tileLayer(MAPTILER_STREETS_URL, {
maxZoom: 18,
minZoom: 2,
attribution: MAPBOX_ATTRIBUTION,
tileSize: 512,
zoomOffset: -1
});
// eslint-disable-next-line
var satelliteTilesLayer = L.tileLayer(MAPTILER_SATELLITE_URL, {
maxZoom: 18,
minZoom: 2,
tileSize: 512,
zoomOffset: -1
});
var baseMaps = {
Satellite: satelliteTilesLayer,
Streets: streetTilesLayer
};
if (myMap != undefined) {
myMap.remove();
}
var myMap = L.map("leafletMapId", {
layers: [streetTilesLayer],
}).setView(this.center, this.zoom);
var markers = L.markerClusterGroup({
iconCreateFunction: function(cluster) {
var count = cluster.getChildCount();
var digits = (count + "").length;
return L.divIcon({
html: "<b>" + count + "</b>" + digits,
className: "cluster digits-" + digits,
iconSize: 22 + 10 * digits
});
}
});
async function fetchLocations(shouldProgrammaticallyFitBounds) {
markers.clearLayers();
markers = L.markerClusterGroup({
chunkedLoading: true,
iconCreateFunction: function(cluster) {
var count = cluster.getChildCount();
var digits = (count + "").length;
return L.divIcon({
html: "<b>" + count + "</b>",
className: "cluster digits-" + digits,
iconSize: 22 + 10 * digits
});
}
});
axios
.get("/maps/" + $this.list.list_id, {
//fetches markerclusters
})
.then(
response => {
$this.tooManyResults = false;
var addressPoints = response.data.recordsList;
for (var i = 0; i < addressPoints.length; i++) {
var a = addressPoints[i];
var title = a[2];
var marker = L.marker(L.latLng(a[0], a[1]));
marker.bindPopup(title);
markers.addLayer(marker); // This is where I'm adding the markers and markerclusters to the layer titled "markers"
// myMap.addLayer(markers); //If I uncomment this, then the markers layer is always on the map, i.e. not as an overlay layer
}
},
error => {
$this.isSearching = false;
document.getElementById("leafletMapId").style.display = "block";
store.setErrorMessage("Network error searching list", error);
}
);
}
myMap.on("zoomend", handlerFunction);
myMap.on("dragend", handlerFunction);
var defaultPolygonStyle = {
color: "black",
weight: 1,
opacity: 0.8,
fillOpacity: 0.1
};
var NationalCongressional = new L.geoJson.ajax(
"http://localhost:8080/assets/geo/NationalCongressional.json",
{
style: defaultPolygonStyle,
onEachFeature: function(feature, layer) {
layer.bindPopup(feature.properties.NAMELSAD);
if (feature.properties.NAMELSAD == "Congressional District 8") {
layer.setStyle({ color: "orange" });
}
}
}
);
function getColorFromRedBlueRange(d) {
return d > 0.8
? "#FF0000"
: d > 0.7
? "#FF006F"
: d > 0.55
? "#FF00EF"
: d > 0.45
? "#DE00FF"
: d > 0.3
? "#BC00FF"
: d > 0.2
? "#6600FF"
: "#00009FF";
}
var ILZipCodes = new L.geoJson.ajax(
"https://raw.githubusercontent.com/OpenDataDE/State-zip-code-GeoJSON/master/il_illinois_zip_codes_geo.min.json",
{
// style: defaultPolygonStyle,
onEachFeature: function(feature, layer) {
layer.bindPopup(
feature.properties.ZCTA5CE10 +
", " +
$this.zipData[feature.properties.ZCTA5CE10]
);
layer.setStyle({
color: getColorFromRedBlueRange(
$this.zipData[feature.properties.ZCTA5CE10]
),
weight: 0,
fillOpacity: 0.3
});
}
}
);
var overlays = {
Voters: voterGroup,
ILZipCodes: ILZipCodes,
};
L.control.layers(baseMaps, overlays).addTo(myMap);
fetchLocations(true);
}
}
};
You add voterGroup to your Layers Control, instead of your markers.
Then simply do not re-assign a MarkerClusterGroup into your markers variable (after you use clearLayers) and you should be fine.

data is shown on chartjs-vue only after resizing browser or opening developer tools

im having hard time fixing this problem.
i get my data with a get request, after opening developer tools or resizing browser the data is shown.
i have tried some delay or timeout solutions but sadly could not make it work. i need the fastest solution, even if it is a "dirty hack" sort of solution.
im using vue 2.9.1 and chartjs-vue 2.7.1.
thank you!
this is my code:
import {Line} from 'vue-chartjs'
export default {
name: 'Events',
extends: Line,
created: function () {
this.getEvents();
},
data () {
return {
gradient: null,
gradient2: null,
datesList : [],
avgList: []
}
},
methods:{
graphClickEvent(event, array){
var points = this.getElementAtEvent(event)
},
getEvents () {
this.$http.get('http://localhost:8081/users/getAllEvents'
,{headers: {'Content-Type': 'application/json',
'Authorization': localStorage.getItem('token'),}
}).then((res) => {
// res.body = array of event object
var eventsArr = res.body;
var arrayLength = eventsArr.length;
for (var i = 0; i < arrayLength; i++) {
var date = new Date(parseInt(eventsArr[i].startTime))
var day = date.getDate()
var month = date.getMonth()
var year = date.getFullYear()
var hours = date.getHours()
hours = ("0" + hours).slice(-2);
var minutes = date.getMinutes()
minutes = ("0" + minutes).slice(-2);
var str = day;
var str = day + "." + (month + 1) + "." + year +" - " + hours + ":" + minutes
this.datesList.push(str);
}
for (var i = 0; i < arrayLength; i++) {
var evnt = eventsArr[i];
this.avgList.push({label: evnt.name,y: evnt.pulseAverage ,tag: evnt.tag, id: evnt.id });
}
})
}
},
mounted () {
this.gradient = this.$refs.canvas.getContext('2d').createLinearGradient(0, 0, 0, 450)
this.gradient2 = this.$refs.canvas.getContext('2d').createLinearGradient(0, 0, 0, 450)
this.gradient.addColorStop(0, 'rgba(255, 0,0, 0.5)')
this.gradient.addColorStop(0.5, 'rgba(255, 0, 0, 0.25)');
this.gradient.addColorStop(1, 'rgba(255, 0, 0, 0)');
this.gradient2.addColorStop(0, 'rgba(0, 231, 255, 0.9)')
this.gradient2.addColorStop(0.5, 'rgba(0, 231, 255, 0.35)');
this.gradient2.addColorStop(1, 'rgba(0, 231, 255, 0)');
this.renderChart({
labels: this.datesList,
datasets: [
{
label: 'Events',
borderColor: '#05CBE1',
pointBackgroundColor: 'white',
pointBorderColor: 'white',
borderWidth: 2,
backgroundColor: this.gradient2,
data: this.avgList
},
]
,
options: {
scales: {
xAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
}
//
,{ onClick: function(event){
var activePoints = this.getElementAtEvent(event)
var firstPoint = activePoints[0];
if(firstPoint !== undefined){
var label = this.data.labels[firstPoint._index];
var value = this.data.datasets[firstPoint._datasetIndex].data[firstPoint._index];
var location = "eventGraph?id=" + value.id;
window.location.href = location;
}
}
, responsive: true, maintainAspectRatio: false,fontColor: '#66226',
tooltips: {
enabled: true,
mode: 'single',
callbacks: {
title: function(tooltipItems, data) {
var evnt = data.datasets[0].data[tooltipItems[0].index].label;
return evnt
},
label: function(tooltipItems, data) {
var avg = 'Average heart: ' + [tooltipItems.yLabel];
var evnt = 'Type: ' + data.datasets[0].data[tooltipItems.index].tag;
return [avg,evnt];
}
}
}
})
}
}
It sounds like an initialization issue where the chart is initially rendered in an element that is not visible or it can't determine the size of the parent.
I see similar issues doing a Google:
ChartJS won't draw graph inside bootstrap tab until window resize #17
Charts rendered in hidden DIVs will not display until browser resize #29
Chart.js not rendering properly until window resize or toggling line in legend
One suggested doing a chart refresh when everything is visible:
setTimeout(function() { myChart.update(); },1000);
In think in vue this could be done in the mount function.
I'm sure there is a more elegant way to handle this with a watch function or something similar for instance.
Updated:
As mentioned in the comments, I've seen references to people firing a window resize event manually using:
window.dispatchEvent(new Event('resize'));
I think it would probably work in the mounted function of the vue component:
export default {
name: 'graphs-component',
mounted: function(){
window.dispatchEvent(new Event('resize'));
}
}
You might even wrap it in a setTimeout to ensure everything is loaded.

Dynamically update lines in Highcharts time series chart

Here I'm working on Highcharts time series chart with live streaming data, based on the sample jsfiddle. In the fiddle there shows 4 lines named as input1, input2, input3, & input 4 and it is updated with live random data but in my actual project the input values are updated via MQTT. In actual project, sometimes, when we push streaming data, there will be increase or decrease in no: of inputs (such as input5, input6 like wise). So how can we add new line or remove line dynamically in time series chart with streaming data.
javascript code :
$(function() {
$(document).ready(function() {
Highcharts.setOptions({
global: {
useUTC: false
}
});
$('#container').highcharts({
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;
var length = series.length;
setInterval(function() {
var x = (new Date()).getTime(), // current time
a0 = Math.random();
a1 = Math.random();
a2 = Math.random();
series[0].addPoint([x, Math.random()], true, true);
for (var i = 1; i < length; i++) {
series[i].addPoint([x, Math.random()], false, true);
}
}, 1000);
}
}
},
title: {
text: 'Live random data'
},
legend: {
enabled: true
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
formatter: function() {
return '<b>' + this.series.name + '</b><br/>' +
Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' +
Highcharts.numberFormat(this.y, 2);
}
},
exporting: {
enabled: false
},
series: [{
name: 'input1',
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: 'input2',
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: 'input3',
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: 'input4',
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;
}())
}]
});
});
});