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.
Related
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();
};
I'm having an issue with Canvas when trying to create a generated collection of .png images.
I can create the .png files fine but the first image or the image before is not being cleared.
index.js
const fs = require("fs");
const { createCanvas, loadImage } = require("canvas");
const canvas = createCanvas(1000,1000);
const ctx = canvas.getContext("2d");
const edition = 10;
const {layers,width,height} = require("./input/config.js");
const saveLayer = (_canvas, _edition) => {
fs.writeFileSync(`./output/${_edition}.png`, _canvas.toBuffer("image/png"));
};
const drawLayer = async (_layer, _edition) => {
let element = _layer.elements[Math.floor(Math.random() * _layer.elements.length)];
const image = await loadImage(`${_layer.location}${element.fileName}`);
ctx.drawImage(
image,
_layer.position.x,
_layer.position.y,
_layer.size.width,
_layer.size.height
);
console.log(`I created the ${_layer.name} layer, and chose element ${element.name}`);
saveLayer(canvas, _edition);
};
for(let i = 0; i <= edition; i++){
layers.forEach(layer => {
drawLayer(layer, i);
});
console.log("Creating edition " + i);
};
config.js
const fs = require("fs");
const width = 1000;
const height = 1000;
const dir = __dirname;
const rarity = [
{key: "", val: "original" },
{key: "_r", val: "rare" },
{key: "_sr", val: "super rare" },
];
const addRarity = (_str) => {
let itemRarity;
rarity.forEach((r) => {
if (_str.includes(r.key)){
itemRarity = r.val;
}
});
return itemRarity;
};
const cleanName = (_str) => {
let name = _str.slice(0, -4);
return name;
};
const getElements = (path) => {
return fs
.readdirSync(path)
//.filter((item) => !/(^|\/)\.|^\/\.|/g.test(item))
.map((i,index) => {
return {
id: index,
name: cleanName(i),
fileName: i,
rarity: addRarity(i),
};
});
};
const layers = [
{
id: 1,
name: "0",
location: `${dir}/0/`,
elements: getElements(`${dir}/0/`),
position: {x:0, y:0},
size: {width: width, height: height},
},
{
id: 2,
name: "1",
location: `${dir}/1/`,
elements: getElements(`${dir}/1/`),
position: {x:0, y:0},
size: {width: width, height: height},
},
{
id: 3,
name: "2",
location: `${dir}/2/`,
elements: getElements(`${dir}/2/`),
position: {x:0, y:0},
size: {width: width, height: height},
},
{
id: 4,
name: "3",
location: `${dir}/3/`,
elements: getElements(`${dir}/3/`),
position: {x:0, y:0},
size: {width: width, height: height},
},
{
id: 5,
name: "4",
location: `${dir}/4/`,
elements: getElements(`${dir}/4/`),
position: {x:0, y:0},
size: {width: width, height: height},
},
];
//console.log(layers);
module.exports = {layers,width,height};
The code is working fine and I'm able to create 10 .png files.
From the images above you can see the blue hat from Image 1 is still showing in Image 2.
Edit: What I've tried -
ctx.beginPath();
context.clearRect(0, 0, canvas.width, canvas.height);
const createNFTs = async() =>{
for(let i = 1; i <= edition; i++){
for (const layer of layers) await drawLayer(layer, i);
console.log("Creating edition " + i);
};
};
Final Code:
for(let i = 1; i <= edition; i++){
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const layer of layers) await drawLayer(layer, i);
console.log("Creating edition " + i);
};
You have a forEach calling an async function.
forEach and async do not go together: the forEach is drawing the next image before the previous one is finished
Replace
layers.forEach(layer => {
drawLayer(layer, i);
});
with
for (const layer of layers) {
await drawLayer(layer, i);
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
(adding clear)
and put it in an async function
I just began coding in Phaser 3 after developing some games with p5.js and wanted to make a 2d endless runner game. So here's the code for my game:
var game;
var gameOptions = {
monkeyGravity: 1200,
monkeyPower: 1000
}
window.onload = function() {
let gameConfig = {
type: Phaser.AUTO,
backgroundColor:0x87ceeb,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: 'thegame',
width: 1920,
height: 1080
},
physics: {
default: 'arcade',
arcade: {debug: true}
},
scene: [
startGame,
playGame
]
}
game = new Phaser.Game(gameConfig);
window.focus();
}
class startGame extends Phaser.Scene{
constructor(){
super('StartGame');
}
preload(){
this.load.image('menu-bg', '/jeffrey2nd/menu-bg.png');
this.load.image('play-button', '/jeffrey2nd/play-button.png');
}
create() {
this.menuBg = this.add.sprite(game.config.width / 2, 0, 'menu-bg').setScale(2);
const screenCenterX = this.cameras.main.worldView.x + this.cameras.main.width / 2;
const screenCenterY = this.cameras.main.worldView.y + this.cameras.main.height / 2;
const startButton = this.add.sprite(screenCenterX, screenCenterY, 'play-button');
startButton.setInteractive();
startButton.on('pointerdown', () => this.scene.start('PlayGame'));
}
}
class playGame extends Phaser.Scene{
constructor(){
super('PlayGame');
}
preload(){
this.load.image('background', '/jeffrey2nd/background.png');
this.load.image('backgroundL1', '/jeffrey2nd/backgroundL1.png');
this.load.image('backgroundL2', '/jeffrey2nd/backgroundL2.png');
this.load.image('backgroundL3', '/jeffrey2nd/backgroundL3.png');
this.load.image('backgroundL4', '/jeffrey2nd/backgroundL4.png');
this.load.image('ground', '/jeffrey2nd/ground.png');
//ANIMATIONS
this.load.image('run0', '/jeffrey2nd/animations/monkey/Running_000.png');
this.load.image('run1', '/jeffrey2nd/animations/monkey/Running_001.png');
this.load.image('run2', '/jeffrey2nd/animations/monkey/Running_002.png');
this.load.image('run3', '/jeffrey2nd/animations/monkey/Running_003.png');
this.load.image('run4', '/jeffrey2nd/animations/monkey/Running_004.png');
this.load.image('run5', '/jeffrey2nd/animations/monkey/Running_005.png');
this.load.image('run6', '/jeffrey2nd/animations/monkey/Running_006.png');
this.load.image('run7', '/jeffrey2nd/animations/monkey/Running_007.png');
this.load.image('run8', '/jeffrey2nd/animations/monkey/Running_008.png');
this.load.image('run9', '/jeffrey2nd/animations/monkey/Running_009.png');
this.load.image('run10', '/jeffrey2nd/animations/monkey/Running_010.png');
this.load.image('run11', '/jeffrey2nd/animations/monkey/Running_011.png');
this.load.image('run12', '/jeffrey2nd/animations/monkey/Running_012.png');
this.load.image('run13', '/jeffrey2nd/animations/monkey/Running_013.png');
this.load.image('jump0', '/jeffrey2nd/animations/monkey/Jumping_000.png');
this.load.image('jump1', '/jeffrey2nd/animations/monkey/Jumping_001.png');
this.load.image('jump2', '/jeffrey2nd/animations/monkey/Jumping_002.png');
this.load.image('jump3', '/jeffrey2nd/animations/monkey/Jumping_003.png');
this.load.image('jump4', '/jeffrey2nd/animations/monkey/Jumping_004.png');
}
create(){
//BACKGROUND AND LAYERS
this.bgLs = this.add.group();
this.background = this.bgLs.create(0, game.config.height / 2, 'background');
this.backgroundL1 = this.bgLs.create(0, game.config.height / 2, 'backgroundL1');
this.backgroundL12 = this.bgLs.create(game.config.width, game.config.height / 2, 'backgroundL1');
this.backgroundL2 = this.bgLs.create(0, game.config.height / 2, 'backgroundL2');
this.backgroundL22 = this.bgLs.create(game.config.width, game.config.height / 2, 'backgroundL2');
this.backgroundL3 = this.bgLs.create(0, game.config.height / 2, 'backgroundL3');
this.backgroundL32 = this.bgLs.create(game.config.width, game.config.height / 2, 'backgroundL3');
this.backgroundL4 = this.bgLs.create(0, game.config.height / 2, 'backgroundL4');
this.backgroundL42 = this.bgLs.create(game.config.width, game.config.height / 2, 'backgroundL4');
for (let b of this.bgLs.children.entries) {
b.setOrigin(0, 0.5);
}
//GROUND PLATFORMS
this.groundGroup = this.physics.add.group();
this.ground1 = this.groundGroup.create(0, game.config.height - 50, 'ground');
this.ground2 = this.groundGroup.create(game.config.width, game.config.height - 50, 'ground');
this.ground3 = this.groundGroup.create(game.config.width + game.config.width / 2, game.config.height - 275, 'ground');
this.ground4 = this.groundGroup.create(game.config.width + game.config.width / 1.5, game.config.height - 500, 'ground');
this.ground4.setScale(0.5, 1);
for (let g of this.groundGroup.children.entries) {
g.setOrigin(0, 0.5);
g.setImmovable(true);
g.setScale(1,0.3);
g.body.checkCollision.down = false;
}
//MONKEY
this.monkey = this.physics.add.sprite(game.config.width / 10 * 2, 500, 'run0');
this.monkey.setScale(0.3);
this.anims.create({
key: "player-run",
frames: [
{ key: 'run0' },
{ key: 'run1' },
{ key: 'run2' },
{ key: 'run3' },
{ key: 'run4' },
{ key: 'run5' },
{ key: 'run6' },
{ key: 'run7' },
{ key: 'run8' },
{ key: 'run9' },
{ key: 'run10' },
{ key: 'run11' },
{ key: 'run12' },
{ key: 'run13' }
],
frameRate: 20,
repeat: -1
})
this.anims.create({
key: "player-jump",
frames: [
{ key: 'jump0' },
{ key: 'jump1' },
{ key: 'jump2' },
{ key: 'jump3' },
{ key: 'jump4' }
],
frameRate: 20,
repeat: -1
})
this.monkey.body.setSize(this.monkey.width/2, this.monkey.height/2);
this.input.on('pointerdown', this.jump, this);
this.input.on('pointerup', this.fall, this);
}
update(){
this.backgroundL1.x -= 0.2;
this.backgroundL12.x -= 0.2;
this.backgroundL2.x -= 0.4;
this.backgroundL22.x -= 0.4;
this.backgroundL3.x -= 0.6;
this.backgroundL32.x -= 0.6;
this.backgroundL4.x -= 0.8;
this.backgroundL42.x -= 0.8;
for (let b of this.bgLs.children.entries) {
if (b.x <= -game.config.width) b.setX(game.config.width);
}
var speed = 5;
for (let g of this.groundGroup.children.entries) {
g.setX(g.x-speed);
//if (g.x <= -game.config.width) g.setX(game.config.width);
}
if (this.ground1.x <= -game.config.width) this.ground1.setX(game.config.width);
if (this.ground2.x <= -game.config.width) this.ground2.setX(game.config.width);
if (this.ground3.x <= -game.config.width) {
this.rnd1 = (Phaser.Math.Between(0, 500))/100;
this.ground3.setX(game.config.width + game.config.width / this.rnd1);
}
if (this.ground4.x <= -game.config.width) {
this.rnd2 = (Phaser.Math.Between(0, 500))/100;
this.ground4.setX(game.config.width + game.config.width / this.rnd2);
}
this.physics.world.collide(this.groundGroup, this.monkey, ()=> {console.log('touche' + game.loop.time )}, null, this);
this.monkey.body.gravity.y = gameOptions.monkeyGravity;
if(this.monkey.body.touching.down) this.monkey.anims.play("player-run", true);
else this.monkey.anims.play("player-jump", true);
}
jump(){
if(this.monkey.body.touching.down) {
this.monkey.body.setVelocity(0, -gameOptions.monkeyPower);
this.monkey.anims.stop();
this.monkey.anims.play("player-jump");
}
}
fall(){
if (this.monkey.body.velocity.y < 0) this.monkey.body.setVelocity(0, -100);
}
}
Game start scene has the Start Button, the game begins, the monkey runs on the platforms and you can jump on the upper platforms.
All seems to work fine, but sometimes randomly the monkey falls down off the screen.
You can see a playable version of the bug at https://420videogames.com/jeffrey2nd
Here I added a console log in the 'monkey vs ground goup collide' callback function, logging the game.loop.time to try to understand. My idea was that maybe some frames were missed during Update and the objects did not collide perfectly, but when the monkey falls off, the callback function runs 2 times and then the monkey keeps falling and the game breaks up.
Another strange thing about this issue is that on my mobile phone REDMI8 the game works with no problems, as for the iPhone8 of my GF. On Firefox mobile of another friend, by the way, the game has the same PC issue.
Thank you in advance for your attention, hope someone can help me fix this problem,
Ab
The issue was resolved moving the colliders from Update function to Create function.
I am trying to render a complex network using the React-Sigma wrapper. I know the base structure of this network to be many nodes of degree one and some of a very high degree. To this end, I have preformatted my graph data with x and y coordinates that represent an approximate layout, with degree one nodes clustered around the nodes they are connected to. I then want to run the ForceAtlas2 simulation from this point. However, when I try to do this, the simulation appears to just randomise and change the initial positions of the nodes to be in the centre, as seen in this gif:
Is there any way to stop this from happening and run the simulation from the initial positions? My code is below:
const App = () => {
const myGraph = getExampleGraph();
return (
<Sigma
graph={myGraph}
renderer="webgl"
settings={{
clone: false,
batchEdgesDrawing: true,
}}
style={{
height: "100vh"
}}
>
<ForceAtlas2
iterationsPerRender={1}
barnesHutOptimize
barnesHutTheta={1}
timeout={50000}
worker
/>
</Sigma>
)
}
Code to get a random example graph like the one I described:
export const getRandomInt = (max: number) => Math.floor(Math.random() * Math.floor(max));
export const getRandomPosition = () => ({
x: Math.random(),
y: Math.random()
});
export const randomisePosition = (position) => ({
x: position.x + (Math.random() - .5)/10,
y: position.y + (Math.random() - .5)/10
});
export const getExampleGraph = () => {
const positions: any = {
"1": getRandomPosition(),
"2": getRandomPosition(),
"3": getRandomPosition()
};
const highDegNodes = [
{ id: "1", size: 20, label: "1", ...positions["1"]},
{ id: "2", size: 20, label: "2", ...positions["2"] },
{ id: "3", size: 20, label: "3", ...positions["3"] }
];
const nodes = Object.assign([], highDegNodes);
const edges = [];
for(let i = 0; i < 50; i += 1) {
const id = (i + 4) + '';
const node = { id, size: 1, label: id, x: 0, y: 0 };
const target = (getRandomInt(3) + 1) + '';
edges.push({
id: `${id}:${target}:${i}`,
source: id,
target,
});
const nodePos = randomisePosition(positions[target]);
node.x = nodePos.x;
node.y = nodePos.y;
nodes.push(node);
if (Math.random() < .1) {
const target2 = (getRandomInt(3) + 1) + '';
edges.push({
id: `${id}:${target2}:${i}:2`,
source: id,
target: target2,
});
}
}
return {
nodes,
edges
}
};
The problem there is with implementation of ForceAtlas2 used in Sigma.js. It expects a higher scale of positions and is highly unstable at scale under 1.
The easiest to stabilize is you can multiply your positions by 100:
const getRandomPosition = () => ({
x: Math.random()*100,
y: Math.random()*100
});
const randomisePosition = (position) => ({
x: position.x + (Math.random() - .5)*10,
y: position.y + (Math.random() - .5)*10
});
you could also apply slowDown={10} to forceAtlas to make it softer and remove batchEdgesDrawing if your graph is not too big:
<Sigma
graph={myGraph}
renderer="webgl"
settings={{
clone: false,
}}
style={{
height: "100vh"
}}
>
<ForceAtlas2
iterationsPerRender={1}
barnesHutOptimize
barnesHutTheta={1}
slowDown={10}
timeout={2000}
worker
/>
</Sigma>
Could anyone please help me in creating a stepper function that would take a parameter 'n' and would increment my slider according to n's value. Circular slider I'm using is this :
https://github.com/fseehawer/react-circular-slider
This is how the knob is being set based on different angles, but what if I want to set the knob at angles separated with 20 degree angle.
const setKnobPosition = useCallback(
(radians) => {
const radius = state.radius - trackSize / 2;
const offsetRadians = radians + knobOffset[knobPosition];
const degrees = (offsetRadians > 0 ? offsetRadians : 2 * Math.PI + offsetRadians)
* (spreadDegrees / (2 * Math.PI));
const pointsInCircle = (state.angles.length - 1) / spreadDegrees;
const currentPoint = Math.round(degrees * pointsInCircle);
if (state.angles[currentPoint] !== state.currentPoint) {
onChange(state.angles[currentPoint]);
}
dispatch({
type: 'setKnobPosition',
payload: {
displayedCompassAngle: state.angles[currentPoint],
knob: {
x: radius * Math.cos(radians) + radius,
y: radius * Math.sin(radians) + radius,
},
},
});
},
[
state.radius,
state.angles,
state.displayedCompassAngle,
knobPosition,
trackSize,
direction,
onChange,
],
);
These are initial state and different props I am using.
const CircularSlider = (props) => {
const {
width = 280,
direction = 1,
knobPosition = 'top',
hideLabelValue = false,
knobDraggable = true,
trackSize = 8,
angles = [],
compassAngle = 0,
onChange = () => {},
} = props;
const initialState = {
mounted: false,
isDragging: false,
width,
radius: width / 2,
knobPosition,
displayedCompassAngle: 0,
angles,
radians: 0,
offset: 0,
knob: {
x: 0,
y: 0,
},
};