I have a webpage with a custom price slider. For desktop it works just fine and when I check it in google developer tools for mobile, it works too, but when I open the webpage with my phone, slider stops working properly, it does not change the plan column. How can I debug something like that?
http://sagemailer-17.eugeneskom.com/
that's the code being used for the slider
window.onload = () => {
const init = function() {
const breakpoints = [{
value: 0,
step: 12.5,
stepsSoFar: 0
},
{
value: 200,
step: 50,
stepsSoFar: 16
},
{
value: 1000,
step: 50,
stepsSoFar: 32
},
{
value: 2000,
step: 500,
stepsSoFar: 52
},
{
value: 10000,
step: 1000,
stepsSoFar: 68
},
{
value: 30000,
step: 5000,
stepsSoFar: 88
},
{
value: 100000,
step: 10000,
stepsSoFar: 102
},
{
value: 300000,
step: 50000,
stepsSoFar: 122
},
{
value: 1000000,
stepsSoFar: 136,
step: 1
}
];
const pricing = [
[200, 4, 0.2],
[500, 10, 0.01],
[1000, 15, 0.01],
[2000, 20, 0.005],
[7000, 50, 0.005],
[10000, 65, 0.0025],
[16000, 80, 0.0022],
[25000, 100, 0.006],
[30000, 130, 0.002],
[65000, 200, 0.002],
[100000, 270, 0.0015],
[200000, 420, 0.0015],
[300000, 570, 0.0006],
[600000, 750, 0.0006],
[700000, 810, 0.0006],
[800000, 870, 0.0006],
[900000, 930, 0.0006],
[1000000, 990, 0.001]
];
const planBgs = document.querySelectorAll(".StepRangeSlider__trackHeadItem");
const media = window.matchMedia("(max-width: 1024px)");
const minValue = 200;
const maxStep = breakpoints[breakpoints.length - 1].stepsSoFar;
const slider = document.getElementById("stepRangeSliderWrap");
const handle = document.getElementById("rangeSliderHandle");
const tooltip = document.getElementById("rangeSliderTooltip");
const plans = document.querySelectorAll(".plan-content .right .content");
let plansBreakPoints;
let valueBlock;
let emailsBlock;
let isHorizontal;
let value = 200;
let step = 18;
let pressed = false;
const checkRangeSliderVersion = () => {
if (media.matches) {
valueBlock = document.querySelectorAll(".display-price-mob");
emailsBlock = document.querySelectorAll(".emails-count");
isHorizontal = false;
} else {
valueBlock = document.querySelectorAll(".display-price");
emailsBlock = document.querySelectorAll(".emails-count");
isHorizontal = true;
}
plansBreakPoints = [
planBgs[1].getBoundingClientRect()[isHorizontal ? "left" : "top"],
planBgs[2].getBoundingClientRect()[isHorizontal ? "left" : "top"]
];
};
checkRangeSliderVersion();
media.addListener(checkRangeSliderVersion);
const getPriceForEmailsCount = emails => {
for (let i = pricing.length - 1; i >= 0; i--) {
if (emails === pricing[i][0]) {
return pricing[i][1];
}
if (emails > pricing[i][0]) {
return (emails - pricing[i][0]) * pricing[i + 1][2] + pricing[i][1];
}
}
return null;
};
const getValueForStep = step => {
const nearest = breakpoints.reduce((prev, curr) =>
curr.stepsSoFar < step && curr.stepsSoFar > prev.stepsSoFar ?
curr :
prev
);
const additionalValue = (step - nearest.stepsSoFar) * nearest.step;
return nearest.value + additionalValue;
};
const handleChange = () => {
const offset = (step / maxStep) * 100;
handle.style[isHorizontal ? "left" : "top"] = offset + "%";
tooltip.textContent = Math.floor(value);
valueBlock.forEach(e => {
e.textContent = getPriceForEmailsCount(value) + "$";
});
emailsBlock.forEach(e => {
e.textContent = Math.floor(value);
});
};
const handleMove = e => {
const client = isHorizontal ? e.clientX : e.clientY;
const sliderRect = slider.getBoundingClientRect();
let startPosition = isHorizontal ? sliderRect.left : sliderRect.top;
let endPosition = isHorizontal ? sliderRect.right : sliderRect.bottom;
if (client <= plansBreakPoints[0]) {
plans.forEach(e => {
e.style.display = "none";
});
plans[0].style.display = "block";
} else if (
client >= plansBreakPoints[0] &&
client <= plansBreakPoints[1]
) {
plans.forEach(e => {
e.style.display = "none";
});
plans[1].style.display = "block";
} else if (client >= plansBreakPoints[1]) {
plans.forEach(e => {
e.style.display = "none";
});
plans[2].style.display = "block";
}
if (!client) return;
let position;
if (client < startPosition) {
position = 0;
} else if (client > endPosition) {
position = endPosition - startPosition;
} else {
position = client - startPosition;
}
const currentStep = Math.round(
(position / (isHorizontal ? sliderRect.width : sliderRect.height)) *
maxStep
);
const currentStepValue = getValueForStep(currentStep);
if (
currentStepValue >= minValue &&
(currentStepValue !== value || currentStep !== step)
) {
value = currentStepValue;
step = currentStep;
handleChange();
}
};
const handleTouchMove = e => {
if (pressed) {
handleMouseMove(e.touches[0]);
}
};
const handleMouseUp = e => {
if (pressed) {
pressed = false;
}
};
const handleMouseMove = e => {
if (pressed) {
handleMove(e);
}
};
window.addEventListener("touchmove", handleTouchMove);
window.addEventListener("touchend", handleMouseUp);
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
slider.addEventListener("mousedown", function(e) {
e.preventDefault();
pressed = true;
handleMove(e);
});
slider.addEventListener("touchmove", e => {
e.preventDefault();
pressed = true;
handleMove(e.touches[0]);
});
handle.addEventListener("mousedown", function(e) {
e.preventDefault();
pressed = true;
handleMove(e);
});
handle.addEventListener("ontouchstart", function(e) {
e.preventDefault();
pressed = true;
handleMove(e.touches[0]);
});
};
init();
};
Related
I have 3 elements with ports. My goal is to not allow linking ports between source and destination elements if one of their ports are already linked to each other. Please see my visualization below:
As you can see, I need only 1 to 1 connection between cells. How do I acheive this with JointJS? Please see my 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);
AddTable(150, 180);
});
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) {
// Prevent link to self
if (cellViewS === cellViewT)
return false;
// Prevent linking from input ports
if ((magnetS !== magnetT))
return true;
},
snapLinks: {
radius: 20
},
defaultLink: () => new joint.shapes.standard.Link({
router: {
name: 'manhattan'
},
connector: {
name: 'normal'
},
attrs: {
line: {
stroke: 'black',
strokeWidth: 1,
sourceMarker: {
'type': 'path',
'stroke': 'black',
'fill': 'black',
'd': 'M 10 -5 0 0 10 5 Z'
},
targetMarker: {
'type': 'path',
'stroke': 'black',
'fill': 'black',
'd': 'M 10 -5 0 0 10 5 Z'
}
}
}
})
});
//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, portID = 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!
If you add another condition to validateConnection, it should do the trick.
If you compare the links of the source and target, then return false if they already contain a link with the same ID.
The following seems to work well in your JSFiddle.
validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
// Prevent link on body
if (magnetT == null)
return false;
// Prevent link to self
if (cellViewS === cellViewT)
return false;
const sourceCell = cellViewS.model;
const sourceLinks = graph.getConnectedLinks(sourceCell);
const targetCell = cellViewT.model;
const targetLinks = graph.getConnectedLinks(targetCell);
let isConnection;
// Compare link IDs of source and target elements
targetLinks.forEach((linkT) => {
sourceLinks.forEach((linkS) => {
if (linkS.id === linkT.id) isConnection = true;
});
});
// If source and target already contain a link with the same id , return false
if (isConnection) return false;
// Prevent linking from input ports
if ((magnetS !== magnetT))
return true;
},
I have created a Bar chart in a react application. This chart is dynamic and by checking each checkbox a set of data needs to be uploaded.
Problem: data is being updated and displayed on the chart, however when I click on that specific checkbox nothing happens and chart does not get back to the original state. I tried Toggling the state of checkboxes but problem is still there and chart is not being updated.
This is my code:
import React, { useEffect, useState } from "react";
import { Bar } from "react-chartjs-2";
const RandomStatic = (props) => {
let jsonData = [
{ gender: "female" },
{ gender: "male" },
{ gender: "female" },
{ gender: "male" },
{ gender: "female" },
{ gender: "male" },
{ gender: "female" },
{ gender: "female" },
{ gender: "female" },
{ gender: "female" },
];
const [userFemaleCounter, setUserFemaleCounter] = useState();
const [userMaleCounter, setUserMaleCounter] = useState();
const [totalCounter, setTotalCounter] = useState();
const [femalePercentage, setFemalePercentage] = useState();
const [malePercentage, setMalePercentage] = useState();
const [chartData, setChartData] = useState(jsonData);
const [femaleIsChecked, setFemaleIsChecked] = useState(false);
const [maleIsChecked, setMaleIsChecked] = useState(false);
useEffect(() => {
// Getting all Data in an array
let allData = jsonData.map(function (e) {
return e.gender;
});
console.log(allData);
// Display Gender data on the chart
let fCounter = [];
let mCounter = [];
allData.forEach((gender) => {
if (gender === "female") {
fCounter = ++fCounter;
} else if (gender === "male") {
mCounter = ++mCounter;
}
});
let userFemaleCounter = fCounter;
setUserFemaleCounter(fCounter);
console.log(userFemaleCounter);
let userMaleCounter = mCounter;
setUserMaleCounter(mCounter);
console.log(userMaleCounter);
let totalCounter = fCounter + mCounter;
setTotalCounter(totalCounter);
console.log(totalCounter);
let femalePercentage = (fCounter / totalCounter) * 100;
setFemalePercentage(femalePercentage);
let malePercentage = (mCounter / totalCounter) * 100;
setMalePercentage(malePercentage);
}, [chartData]);
useEffect(() => {
///change the jsonData
//setChartData
let allData = chartData.map(function (e) {
return e.gender;
});
let mCounter = [];
let fCounter = [];
allData.forEach((gender) => {
if (gender === "female") {
fCounter = ++fCounter;
} else if (gender === "male") {
mCounter = ++mCounter;
}
});
let totalCounter = fCounter + mCounter;
setTotalCounter(totalCounter);
let userFemaleCounter = fCounter;
setUserFemaleCounter(userFemaleCounter);
let femalePercentage = (fCounter / totalCounter) * 100;
setFemalePercentage(femalePercentage);
let userMaleCounter = 0;
setUserMaleCounter(userMaleCounter);
let malePercentage = 0;
setMalePercentage(malePercentage);
}, [femaleIsChecked]);
//useeffect for MALE checkbox
useEffect(() => {
let allData = jsonData.map(function (e) {
return e.gender;
});
let mCounter = [];
let fCounter = [];
allData.forEach((gender) => {
if (gender === "female") {
fCounter = ++fCounter;
} else if (gender === "male") {
mCounter = ++mCounter;
}
});
let totalCounter = fCounter + mCounter;
setTotalCounter(totalCounter);
let malePercentage = (mCounter / totalCounter) * 100;
setMalePercentage(malePercentage);
fCounter = [];
let femalePercentage = [];
setFemalePercentage(femalePercentage);
}, [maleIsChecked]);
const toggleFemale = () => {
if(femaleIsChecked == true)
{
setFemaleIsChecked(false)
}else{
setFemaleIsChecked(true)
}
}
const toggleMale = () => setMaleIsChecked(chartData)
const toggleAll = () => setChartData(!chartData)
//console.log(toggleFemale)
return (
<div>
<Bar
className="chart"
data={{
labels: ["Female", "Male"],
datasets: [
{
data: [femalePercentage, malePercentage],
backgroundColor: ["green", "yellow"],
borderColor: ["green", "yellow"],
borderWidth: 0.5,
},
],
}}
options={{
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: "Male and Female Ratio",
},
legend: {
display: false,
},
},
scales: {
y: {
display: true,
title: {
display: true,
text: "Percentage",
},
},
x: {
display: true,
title: {
display: true,
text: "Population",
},
},
},
showTooltips: false,
hover: false,
}}
/>
<div className="checkbox-form-wrapper">
<div className="chartBox">
<label>Female Only</label>
<input
name="femalePercentage"
type="checkbox"
//checked={femaleIsChecked}
onChange={toggleFemale}
/>
<label>Male Only</label>
<input
name="malePercentage"
type="checkbox"
checked={maleIsChecked}
onChange={toggleMale}
/>
<label>Both Female and Male</label>
<input
name="chartData"
type="checkbox"
checked={chartData}
onChange={toggleAll}
/>
</div>
</div>
</div>
);
};
export default RandomStatic;
Thank you :)
In order to make the code above work as you would expect it, the main changes needed are:
add guards in each Effect to run only when that flag is true. Example for maleIsChecked:
if(!maleIsChecked) { return; }
for the Effect displaying both, make sure it runs only if both flags are off:
if(femaleIsChecked || maleIsChecked) { return;}
and that it runs on every change
useEffect(() => { ... }, [chartData, maleIsChecked, femaleIsChecked]):
update toggle functions to switch "correctly". Example for toggleMale:
const toggleMale = () => setMaleIsChecked(!maleIsChecked)
However, I strongly recommend to switch the approach from checkboxes to radio buttons. For me, this seems more natural as interaction, because you have all the options "on the table" and they are mutually exclusive. Working code suggested below, changes highlighted with CHANGE:
import React, { useEffect, useState } from "react";
import { Bar } from "react-chartjs-2";
// CHANGE: define an enum to handle data types to be displayed
const TypesOfDataToDisplay = {
MALE: 'male',
FEMALE: 'female',
BOTH: 'both'
}
const RandomStatic = (props) => {
let jsonData = [
{ gender: "female" },
{ gender: "male" },
{ gender: "female" },
{ gender: "male" },
{ gender: "female" },
{ gender: "male" },
{ gender: "female" },
{ gender: "female" },
{ gender: "female" },
{ gender: "female" },
];
const [userFemaleCounter, setUserFemaleCounter] = useState();
const [userMaleCounter, setUserMaleCounter] = useState();
const [totalCounter, setTotalCounter] = useState();
const [femalePercentage, setFemalePercentage] = useState();
const [malePercentage, setMalePercentage] = useState();
const [chartData, setChartData] = useState(jsonData);
// CHANGE: define a new state that indicates data type to be displayed
const [dataToDisplay, setDataToDisplay] = useState(TypesOfDataToDisplay.BOTH);
// CHANGE: remove boolean states (using them, for each toggle you will generate two renderings)
//const [femaleIsChecked, setFemaleIsChecked] = useState(false);
//const [maleIsChecked, setMaleIsChecked] = useState(false);
useEffect(() => {
// CHANGE: skip this effect if not needed
if(dataToDisplay !== TypesOfDataToDisplay.BOTH) {
return;
}
// Getting all Data in an array
let allData = jsonData.map(function (e) {
return e.gender;
});
console.log(allData);
// Display Gender data on the chart
let fCounter = [];
let mCounter = [];
allData.forEach((gender) => {
if (gender === "female") {
fCounter = ++fCounter;
} else if (gender === "male") {
mCounter = ++mCounter;
}
});
let userFemaleCounter = fCounter;
setUserFemaleCounter(fCounter);
console.log(userFemaleCounter);
let userMaleCounter = mCounter;
setUserMaleCounter(mCounter);
console.log(userMaleCounter);
let totalCounter = fCounter + mCounter;
setTotalCounter(totalCounter);
console.log(totalCounter);
let femalePercentage = (fCounter / totalCounter) * 100;
setFemalePercentage(femalePercentage);
let malePercentage = (mCounter / totalCounter) * 100;
setMalePercentage(malePercentage);
}, [chartData, dataToDisplay]); // CHANGE: make sure the effect is called on data type change
useEffect(() => {
// CHANGE: skip this effect if not needed
if(dataToDisplay !== TypesOfDataToDisplay.FEMALE) {
return;
}
///change the jsonData
//setChartData
let allData = chartData.map(function (e) {
return e.gender;
});
let mCounter = [];
let fCounter = [];
allData.forEach((gender) => {
if (gender === "female") {
fCounter = ++fCounter;
} else if (gender === "male") {
mCounter = ++mCounter;
}
});
let totalCounter = fCounter + mCounter;
setTotalCounter(totalCounter);
let userFemaleCounter = fCounter;
setUserFemaleCounter(userFemaleCounter);
let femalePercentage = (fCounter / totalCounter) * 100;
setFemalePercentage(femalePercentage);
let userMaleCounter = 0;
setUserMaleCounter(userMaleCounter);
let malePercentage = 0;
setMalePercentage(malePercentage);
}, [dataToDisplay]); // CHANGE: make sure the effect is called on data type change
//useeffect for MALE checkbox
useEffect(() => {
// CHANGE: skip this effect if not needed
if(dataToDisplay !== TypesOfDataToDisplay.MALE) {
return;
}
let allData = jsonData.map(function (e) {
return e.gender;
});
let mCounter = [];
let fCounter = [];
allData.forEach((gender) => {
if (gender === "female") {
fCounter = ++fCounter;
} else if (gender === "male") {
mCounter = ++mCounter;
}
});
let totalCounter = fCounter + mCounter;
setTotalCounter(totalCounter);
let malePercentage = (mCounter / totalCounter) * 100;
setMalePercentage(malePercentage);
fCounter = [];
let femalePercentage = [];
setFemalePercentage(femalePercentage);
}, [dataToDisplay]); // CHANGE: make sure the effect is called on data type change
// CHANGE: remove togglers for the boolean states
/*const toggleFemale = () => {
if(femaleIsChecked == true)
{
setFemaleIsChecked(false)
}else{
setFemaleIsChecked(true)
}
}
const toggleMale = () => setMaleIsChecked(chartData)
const toggleAll = () => setChartData(!chartData)*/
// CHANGE: add only one handler for changing data to be displayed
const toggleDisplay = (event) => {
setDataToDisplay(event.currentTarget.value);
}
// CHANGE: switch from checkboxes to radio buttons and update definitions with new handler
return (
<div className="chartContainer">
<Bar
className="chart"
data={{
labels: ["Female", "Male"],
datasets: [
{
data: [femalePercentage, malePercentage],
backgroundColor: ["green", "yellow"],
borderColor: ["green", "yellow"],
borderWidth: 0.5,
},
],
}}
options={{
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: "Male and Female Ratio",
},
legend: {
display: false,
},
},
scales: {
y: {
display: true,
title: {
display: true,
text: "Percentage",
},
},
x: {
display: true,
title: {
display: true,
text: "Population",
},
},
},
showTooltips: false,
hover: false,
}}
/>
<div className="checkbox-form-wrapper">
<div className="chartBox">
<label>Female Only</label>
<input
name="femalePercentage"
type="radio"
value={TypesOfDataToDisplay.FEMALE}
checked={dataToDisplay === TypesOfDataToDisplay.FEMALE}
onChange={toggleDisplay}
/>
<label>Male Only</label>
<input
name="malePercentage"
type="radio"
value={TypesOfDataToDisplay.MALE}
checked={dataToDisplay === TypesOfDataToDisplay.MALE}
onChange={toggleDisplay}
/>
<label>Both Female and Male</label>
<input
name="chartData"
type="radio"
value={TypesOfDataToDisplay.BOTH}
checked={dataToDisplay === TypesOfDataToDisplay.BOTH}
onChange={toggleDisplay}
/>
</div>
</div>
</div>
);
};
export default RandomStatic;
I am developing a pdf viewer, with the ability to apply additional elements by the user.
Pdf has drag and drop capability, zoom in and out.
I ran into the following problems that baffle me:
Correctly position the new elements so that the image is set at coordinates x: 0, y: 0. (image 1) When displacing pdf, I can not track what distance to displace the coordinates of the new element. (image 2)
To keep the image quality when zooming in, I set the camera Zoom (init scale) to 3. I can't find a solution how to display it in a smaller size when opening the page, so as not to lose quality when zooming in. (image 3)
import React, { useEffect, useState, useRef } from 'react';
import * as PDFJS from 'pdfjs-dist'
import pdfjsWorker from "pdfjs-dist/build/pdf.worker.entry";
import {makeStyles} from "#material-ui/core/styles";
export const PdfCanvas = ({pageNum, editCanvas, colorCircle}) => {
const canvasRef = useRef();
const [image, setImage] = useState();
const fileUri = "https://s3-us-east-2.amazonaws.com/c9e8e9c8-7ec0-412f-81cc-60fc07201419/Bar%20Yohai_Coordination%20plane%2000.pdf";
const mainCanvas = canvasRef.current
const mainCtx = mainCanvas?.getContext('2d');
let cameraOffset = { x: window.innerWidth/2, y: window.innerHeight/2 }
let cameraZoom = 3
let MAX_ZOOM = 108
let MIN_ZOOM = 0.01
let SCROLL_SENSITIVITY = 0.0005
const [positionArr, setPositionArr] = useState([
{id: '1', x: 400, y: 40, radius: 10, color: 'rgb(255,0,0)'},
{id: '2', x: 800, y: 40, radius: 10, color: 'rgb(134,211,17)'},
{id: '3', x: 100, y: 40, radius: 10, color: 'rgb(32,52,157)'},
{id: '4', x: 720, y: 40, radius: 10, color: 'rgb(10,9,9)'},
{id: '5', x: 640, y: 40, radius: 10, color: 'rgb(227,170,24)'},
]);
function isIntersect(point: { x: any; y: any; }, circle: { id?: string; x: any; y: any; radius: any; color?: string; }) {
return Math.sqrt((point.x-circle.x) ** 2 + (point.y - circle.y) ** 2) < circle.radius;
}
const renderPage = (pageNum) => {
const canvas = document.createElement('canvas'), ctx = canvas.getContext('2d');
const container = document.getElementById("container")
if (fileUri) {
const loadingTask = PDFJS.getDocument(fileUri);
loadingTask.promise.then(loadedPdf => {
loadedPdf && loadedPdf.getPage(pageNum).then(function(page) {
const viewport = page.getViewport({scale: cameraZoom});
canvas.width = viewport.width;
canvas.height = viewport.height ;
canvas.style.width = "100%";
canvas.style.height = "100%";
container.style.width = Math.floor(viewport.width/cameraZoom) + 'pt';
container.style.height = Math.floor(viewport.height/cameraZoom) + 'pt';
const renderContext = {
canvasContext: ctx,
viewport: viewport
};
page.render(renderContext).promise.then(() => {
var pdfImage = new Image();
pdfImage.src = canvas.toDataURL("image/png", 1)
setImage(pdfImage)
})
});
}, function (reason) {
console.error(reason);
});
}
};
function draw()
{
if (mainCanvas) {
mainCanvas.width = visualViewport.width
mainCanvas.height = visualViewport.height
}
// Translate to the canvas centre before zooming - so you'll always zoom on what you're looking directly at
if (mainCtx) {
mainCtx.scale(cameraZoom, cameraZoom)
mainCtx.translate( -window.innerWidth / .8 + cameraOffset.x, -window.innerHeight / .8 + cameraOffset.y )
mainCtx.clearRect(0,0, window.innerWidth, window.innerHeight)
mainCtx.fillStyle = "#991111"
if (image) {
mainCtx.drawImage(image, 1, 1);
(positionArr || []).map(circle => {
mainCtx?.beginPath();
mainCtx?.arc(circle.x, circle.y, circle.radius / cameraZoom, 0, 2 * Math.PI, false);
if (mainCtx) {
mainCtx.fillStyle = circle.color
}
mainCtx?.fill();
});
}
}
requestAnimationFrame( draw )
}
// Gets the relevant location from a mouse or single touch event
function getEventLocation(e)
{
if (e.touches && e.touches.length === 1)
{
return { x:e.touches[0].clientX, y: e.touches[0].clientY }
}
else if (e.clientX && e.clientY)
{
return { x: e.clientX, y: e.clientY }
}
}
let isDragging = false
let dragStart = { x: 0, y: 0 }
function onPointerDown(e)
{
isDragging = true
dragStart.x = getEventLocation(e).x/cameraZoom - cameraOffset.x
dragStart.y = getEventLocation(e).y/cameraZoom - cameraOffset.y
}
function onPointerUp(e)
{
isDragging = false
initialPinchDistance = null
lastZoom = cameraZoom
}
function onPointerMove(e)
{
if (isDragging)
{
cameraOffset.x = getEventLocation(e).x/cameraZoom - dragStart.x
cameraOffset.y = getEventLocation(e).y/cameraZoom - dragStart.y
}
}
function handleTouch(e, singleTouchHandler)
{
if ( e.touches.length === 1 )
{
singleTouchHandler(e)
}
else if (e.type === "touchmove" && e.touches.length === 2)
{
isDragging = false
handlePinch(e)
}
}
let initialPinchDistance = null
let lastZoom = cameraZoom
function handlePinch(e)
{
e.preventDefault()
let touch1 = { x: e.touches[0].clientX, y: e.touches[0].clientY }
let touch2 = { x: e.touches[1].clientX, y: e.touches[1].clientY }
// This is distance squared, but no need for an expensive sqrt as it's only used in ratio
let currentDistance = (touch1.x - touch2.x)**2 + (touch1.y - touch2.y)**2
if (initialPinchDistance == null)
{
initialPinchDistance = currentDistance
}
else
{
adjustZoom( null, currentDistance/initialPinchDistance )
}
}
function adjustZoom(zoomAmount, zoomFactor, e)
{
if (!isDragging)
{
if (zoomAmount)
{
cameraZoom += zoomAmount*zoomFactor
}
else if (zoomFactor)
{
cameraZoom = zoomFactor*lastZoom
}
cameraZoom = Math.min( cameraZoom, MAX_ZOOM )
cameraZoom = Math.max( cameraZoom, MIN_ZOOM )
}
}
const handleAddIcon = (event: React.MouseEvent<HTMLCanvasElement, MouseEvent>) => {
if (editCanvas){
const x = event.nativeEvent.offsetX / cameraZoom
const y = event.nativeEvent.offsetY / cameraZoom
console.log(x, y)
const circle = {
id: Math.random().toFixed(2), x, y, radius: 10, color: colorCircle
}
mainCtx?.beginPath();
mainCtx?.arc(circle.x, circle.y, circle.radius, 0, 2 * Math.PI, false);
if (mainCtx) {
mainCtx.fillStyle = circle.color
}
mainCtx?.fill();
const newArr = positionArr;
newArr.push(circle)
setPositionArr(newArr)
} else {
const point = {
x : event.nativeEvent.offsetX / cameraZoom,
y : event.nativeEvent.offsetY / cameraZoom
};
console.log(cameraOffset, "cameraOffset")
console.log(point, "point")
positionArr.forEach(circle => {
if (isIntersect(point, circle)) {
alert('click on circle: ' + circle.id);
}
});
}
};
// Ready, set, go
useEffect(() => {
renderPage(pageNum)
}, [])
draw()
return (
<div id="container">
<canvas
onWheel={(e) => adjustZoom(cameraZoom, e.deltaY*SCROLL_SENSITIVITY, e)}
onMouseMove={onPointerMove}
onMouseUp={onPointerUp}
onTouchStart={(e) => handleTouch(e, onPointerDown)}
onTouchMove={(e) => handleTouch(e, onPointerMove)}
onTouchEnd={(e) => handleTouch(e, onPointerUp)}
onMouseDown={(e) => {onPointerDown(e)}}
onClick={(e) => handleAddIcon(e)}
ref={canvasRef} />
</div>
)
}
image 1
image 2
image 3
I would be very glad if someone can tell me the ways to solve these problems.
I am having issues with a for loop in node.js with express in production, but not in development mode.
For whatever reason when I push to production the xTickValues array is empty, and my for loop is not pushing the values to the array. Why would this not work in production?
Here's the for loop
var daysToCount = 29;
var dayOfYearStart = 91
var xTickValues = [];
// right here
for (let i = 0; i < daysToCount; i++) {
var xTick = i + dayOfYearStart;
xTickValues.push(xTick);
}
console.log(xTickValues, 'this is xtick values')
Here's the result I want for xTickValues. In development the for loop works, but in production it's an empty array
[91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119]
Here's the full file
const User = require("../../models/User");
const { IS_ADMIN, CAN_ACCESS_CHARTS } = require("../../utils/enums");
const { ADMIN_ONLY_MESSAGE } = require("../../utils/defaultErrorMessages");
const isEmpty = require("../../utils/is-empty");
const Validator = require("validator");
const {
intervalToDuration,
eachDayOfInterval,
format: dateFormat,
addDays,
} = require("date-fns");
const getDayOfYearFromDate = require("../../utils/getDayOfYearFromDate");
const validateReqQuery = (data) => {
let errors = {};
data.startDate = !isEmpty(data.startDate) ? data.startDate : "";
if (Validator.isEmpty(data.startDate)) {
errors.startDate = "startDate is required";
}
return {
errors,
isValid: isEmpty(errors),
};
};
module.exports = async (req, res) => {
const { errors, isValid } = validateReqQuery(req.query);
//Check Validation
if (!isValid) {
return res.status(500).json(errors);
}
try {
let adminUser = await User.findOne({ _id: req.user.id }).select(
"permissions"
);
let { limit, startDate, xRangeType = "days", endDate } = req.query;
let tempStartDate = dateFormat(new Date(startDate), "MM/dd/yyyy");
let tempEndDate = addDays(new Date(startDate), 30);
if (req.query.endDate) {
tempEndDate = dateFormat(new Date(endDate), "MM/dd/yyy");
}
let dayOfYearStart = getDayOfYearFromDate(new Date(tempStartDate));
let dayOfYearEnd = getDayOfYearFromDate(new Date(tempEndDate));
let { days: daysToCount } = intervalToDuration({
start: new Date(tempStartDate),
end: new Date(tempEndDate),
});
let xTickFormat = [];
var dayLabelsTemp = eachDayOfInterval({
start: new Date(tempStartDate),
end: new Date(tempEndDate),
});
dayLabelsTemp.forEach((day) => {
let tempDay = dateFormat(day, "MMM-dd");
xTickFormat.push(`${tempDay}`);
});
let dataSets = [];
let dataSetLabels = [];
let xKey = xRangeType || "days";
let yKey = "count";
let xTickValues = [];
let numerator = 0;
let denominator = 0;
// right here
for (let i = 0; i < daysToCount; i++) {
let xTick = i + dayOfYearStart;
xTickValues.push(xTick);
}
if (
adminUser.permissions.includes(IS_ADMIN) ||
adminUser.permissions.includes(CAN_ACCESS_CHARTS)
) {
let totalUsers = await User.find().countDocuments();
let users = await User.find(
{
lastTimeActive: {
$gte: tempStartDate,
$lt: tempEndDate,
},
permissions: {
$nin: [IS_ADMIN],
},
},
function (err, results) {
results.forEach((result) => {
if (
!dataSetLabels.includes(result.lastUsedPlatform) &&
result.lastUsedPlatform
) {
dataSetLabels.push(result.lastUsedPlatform);
}
});
numerator = results.length;
denominator = totalUsers;
}
).select("first_name last_name lastUsedPlatform lastTimeActive");
let tempUserArray = new Array(...users);
dataSetLabels.forEach((label, index) => {
let tempArray = [];
xTickValues.forEach((tick) => {
tempArray.push({
days: tick,
count: 0,
dataSetIndex: index,
});
});
dataSets.push(tempArray);
});
let userItems = [];
tempUserArray = tempUserArray.forEach(
({
first_name,
last_name,
lastUsedPlatform,
_id: userId,
lastTimeActive,
}) => {
let indexToPushTo = xTickValues.find(
(day) => day === getDayOfYearFromDate(lastTimeActive)
);
let dateIndex = dateFormat(lastTimeActive, "MMM-dd");
let dayOfYearIndex = getDayOfYearFromDate(lastTimeActive);
let item = {
dateIndex,
indexToPushTo,
dayOfYearIndex,
name: `${first_name} ${last_name}`,
lastUsedPlatform: lastUsedPlatform,
userId,
parentIndex: dataSetLabels.indexOf(lastUsedPlatform),
};
userItems.push(item);
}
);
userItems.forEach((user, index) => {
let { parentIndex, dayOfYearIndex, lastUsedPlatform } = user;
let columnToPush = dataSets[parentIndex].find(
(item) => item.days === dayOfYearIndex
);
if (columnToPush) {
columnToPush.count = columnToPush.count + 1;
columnToPush.lastUsedPlatform = lastUsedPlatform;
}
});
let payload = {
chartTitle: "Platform Activity",
dataSetLabels,
dataSets,
numerator,
denominator,
xKey,
yKey,
xTickValues,
xTickFormat,
startDate: tempStartDate,
endDate: tempEndDate,
yTickFormat: (x) => x,
};
return res.status(200).json(payload);
} else {
return res.status(401).json({ message: ADMIN_ONLY_MESSAGE });
}
} catch (err) {
let errors = {};
errors.err = err.toString();
errors.endpoint = req.originalUrl;
return res.status(500).json(errors);
}
};
I am trying to get this slider to auto play but I can't seem to get it to workDoes anyone know how I can achieve this?
This is the slideshow:
Slideshow
Replace demo6.js code with given -
{
// From https://davidwalsh.name/javascript-debounce-function.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
class Slideshow {
constructor(el, settings) {
this.DOM = {};
this.DOM.el = el;
this.settings = {
animation: {
slides: {
duration: 500,
easing: 'easeOutQuint'
},
shape: {
duration: 300,
easing: {in: 'easeOutQuint', out: 'easeOutQuad'}
}
},
frameFill: 'url(#gradient1)'
}
this.settings.autoSlide = settings.autoSlide || false;
this.settings.autoSlideTimeout = settings.autoSlideTimeout || 4000;
this.init();
}
init() {
this.DOM.slides = Array.from(this.DOM.el.querySelectorAll('.slides--images > .slide'));
this.slidesTotal = this.DOM.slides.length;
this.DOM.nav = this.DOM.el.querySelector('.slidenav');
this.DOM.titles = this.DOM.el.querySelector('.slides--titles');
this.DOM.titlesSlides = Array.from(this.DOM.titles.querySelectorAll('.slide'));
this.DOM.nextCtrl = this.DOM.nav.querySelector('.slidenav__item--next');
this.DOM.prevCtrl = this.DOM.nav.querySelector('.slidenav__item--prev');
this.current = 0;
this.createFrame();
this.initEvents();
}
createFrame() {
this.rect = this.DOM.el.getBoundingClientRect();
this.frameSize = this.rect.width/12;
this.paths = {
initial: this.calculatePath('initial'),
final: this.calculatePath('final')
};
this.DOM.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
this.DOM.svg.setAttribute('class', 'shape');
this.DOM.svg.setAttribute('width','100%');
this.DOM.svg.setAttribute('height','100%');
this.DOM.svg.setAttribute('viewbox',`0 0 ${this.rect.width} ${this.rect.height}`);
this.DOM.svg.innerHTML = `
<defs>
<linearGradient id="gradient1" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#09012d"/>
<stop offset="100%" stop-color="#0f2b73"/>
</linearGradient>
</defs>
<path fill="${this.settings.frameFill}" d="${this.paths.initial}"/>`;
this.DOM.el.insertBefore(this.DOM.svg, this.DOM.titles);
this.DOM.shape = this.DOM.svg.querySelector('path');
}
updateFrame() {
this.paths.initial = this.calculatePath('initial');
this.paths.final = this.calculatePath('final');
this.DOM.svg.setAttribute('viewbox',`0 0 ${this.rect.width} ${this.rect.height}`);
this.DOM.shape.setAttribute('d', this.isAnimating ? this.paths.final : this.paths.initial);
}
calculatePath(path = 'initial') {
if ( path === 'initial' ) {
return `M 0,0 0,${this.rect.height} ${this.rect.width},${this.rect.height} ${this.rect.width},0 0,0 Z M 0,0 ${this.rect.width},0 ${this.rect.width},${this.rect.height} 0,${this.rect.height} Z`;
}
else {
const point1 = {x: this.rect.width/4-50, y: this.rect.height/4+50};
const point2 = {x: this.rect.width/4+50, y: this.rect.height/4-50};
const point3 = {x: this.rect.width-point2.x, y: this.rect.height-point2.y};
const point4 = {x: this.rect.width-point1.x, y: this.rect.height-point1.y};
return `M 0,0 0,${this.rect.height} ${this.rect.width},${this.rect.height} ${this.rect.width},0 0,0 Z M ${point1.x},${point1.y} ${point2.x},${point2.y} ${point4.x},${point4.y} ${point3.x},${point3.y} Z`;
}
}
initEvents() {
this.DOM.nextCtrl.addEventListener('click', () => this.navigate('next'));
this.DOM.prevCtrl.addEventListener('click', () => this.navigate('prev'));
window.addEventListener('resize', debounce(() => {
this.rect = this.DOM.el.getBoundingClientRect();
this.updateFrame();
}, 20));
document.addEventListener('keydown', (ev) => {
const keyCode = ev.keyCode || ev.which;
if ( keyCode === 37 ) {
this.navigate('prev');
}
else if ( keyCode === 39 ) {
this.navigate('next');
}
});
if(this.settings.autoSlide) {
setInterval(() => this.navigate('next'), this.settings.autoSlideTimeout);
}
}
navigate(dir = 'next') {
if ( this.isAnimating ) return false;
this.isAnimating = true;
const animateShapeIn = anime({
targets: this.DOM.shape,
duration: this.settings.animation.shape.duration,
easing: this.settings.animation.shape.easing.in,
d: this.paths.final
});
const animateSlides = () => {
return new Promise((resolve, reject) => {
const currentSlide = this.DOM.slides[this.current];
anime({
targets: currentSlide,
duration: this.settings.animation.slides.duration,
easing: this.settings.animation.slides.easing,
translateY: dir === 'next' ? this.rect.height : -1*this.rect.height,
complete: () => {
currentSlide.classList.remove('slide--current');
resolve();
}
});
const currentTitleSlide = this.DOM.titlesSlides[this.current];
anime({
targets: currentTitleSlide.children,
duration: this.settings.animation.slides.duration,
easing: this.settings.animation.slides.easing,
delay: (t,i,total) => dir === 'next' ? i*100 : (total-i-1)*100,
translateY: [0, dir === 'next' ? 100 : -100],
opacity: [1,0],
complete: () => {
currentTitleSlide.classList.remove('slide--current');
resolve();
}
});
this.current = dir === 'next' ?
this.current < this.slidesTotal-1 ? this.current + 1 : 0 :
this.current > 0 ? this.current - 1 : this.slidesTotal-1;
const newSlide = this.DOM.slides[this.current];
newSlide.classList.add('slide--current');
anime({
targets: newSlide,
duration: this.settings.animation.slides.duration,
easing: this.settings.animation.slides.easing,
translateY: [dir === 'next' ? -1*this.rect.height : this.rect.height,0]
});
const newSlideImg = newSlide.querySelector('.slide__img');
anime.remove(newSlideImg);
anime({
targets: newSlideImg,
duration: this.settings.animation.slides.duration*3,
easing: this.settings.animation.slides.easing,
translateY: [dir === 'next' ? -100 : 100, 0],
scale: [0.2,1]
});
const newTitleSlide = this.DOM.titlesSlides[this.current];
newTitleSlide.classList.add('slide--current');
anime({
targets: newTitleSlide.children,
duration: this.settings.animation.slides.duration*1.5,
easing: this.settings.animation.slides.easing,
delay: (t,i,total) => dir === 'next' ? i*100+100 : (total-i-1)*100+100,
translateY: [dir === 'next' ? -100 : 100 ,0],
opacity: [0,1]
});
});
};
const animateShapeOut = () => {
anime({
targets: this.DOM.shape,
duration: this.settings.animation.shape.duration,
easing: this.settings.animation.shape.easing.out,
d: this.paths.initial,
complete: () => this.isAnimating = false
});
}
animateShapeIn.finished.then(animateSlides).then(animateShapeOut);
}
};
new Slideshow(document.querySelector('.slideshow'), {
autoSlide: true,
autoSlideTimeout: 4000 // 4 second
});
imagesLoaded('.slide__img', { background: true }, () => document.body.classList.remove('loading'));
};
I have added settings -
new Slideshow(document.querySelector('.slideshow'), {
autoSlide: true,
autoSlideTimeout: 4000 // 4 second
});
You may code diff to see the changes I have made.