Me and my friend have been working on a uni assignment - a simple Pacman clone - in ThreeJS (mandatory) and we've had this problem since pretty much the beginning. As the scene keeps running, it starts going faster and faster with each second. It's not like there becomes less stuff to render, everything is pretty much the same.
import * as THREE from '././../three.js-master/build/three.module.js';
function main() {
const canvas = document.querySelector('#canva');
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setSize(window.innerWidth, window.innerHeight);
/* Camera */
const fov = 40;
const aspect = window.innerWidth / window.innerHeight;
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// I want to go for that angled look
// Angled enough to show depth, but still have everything important visible
camera.position.set(0, 65, -45); //Man setting up the camera is fun and not at all painful
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
/* Lights */
const mainLight = new THREE.DirectionalLight(0xffffff, .85);
mainLight.position.set(0, 20, 0);
scene.add(mainLight);
mainLight.castShadow = true;
mainLight.shadow.mapSize.width = 2048;
mainLight.shadow.mapSize.height = 2048;
// const light = new THREE.PointLight(0xffffff, 1);
// light.position.set(30, 0, 30);
// scene.add(light);
/* Board */
const boardGeometry = new THREE.PlaneGeometry(50, 50);
const boardMaterial = new THREE.MeshToonMaterial({ color: 0xEEEEEE, side: THREE.DoubleSide });
const board = new THREE.Mesh(boardGeometry, boardMaterial);
board.rotation.x = Math.PI / 2; //The board must be placed flat on the x axis
scene.add(board);
/* Player */
const playerBox = new THREE.Box3() // Used to determine collisions
const playerGeometry = new THREE.BoxGeometry(1, 1, 1.5);
const playerMaterial = new THREE.MeshToonMaterial({ color: 0xAAAAAA });
const player = new THREE.Mesh(playerGeometry, playerMaterial);
player.geometry.computeBoundingBox(playerBox);
/* Players Axes helper */
const axesHelper = new THREE.AxesHelper(5);
player.add(axesHelper);
scene.add(player);
/* Box helper */
const playerHelper = new THREE.Box3Helper(playerBox, 0xffff00);
scene.add(playerHelper);
/* Enemy #1 */
const enemy1Box = new THREE.Box3()
const enemy1Geometry = new THREE.BoxGeometry(1, 1, 1);
const enemy1Material = new THREE.MeshToonMaterial({ color: 0x0000ff });
const enemy1 = new THREE.Mesh(enemy1Geometry, enemy1Material);
enemy1.position.x = 2
enemy1.geometry.computeBoundingBox(enemy1Box);
scene.add(enemy1);
const enemyHelper = new THREE.Box3Helper(enemy1Box, 0xffff00);
scene.add(enemyHelper);
/////////////////////
/* Cheese */
const smallCollectibleRadius = .4
const bigCollectibleRadius = .6
/* Standard cheese */
var cheeseList = []
var cheeseBoxList = []
const cheeseBox = new THREE.Box3();
const cheeseGeometry = new THREE.SphereGeometry(smallCollectibleRadius, 100, 100);
const cheeseMaterial = new THREE.MeshToonMaterial({ color: 0xffff00, emissive: 0xffff00 });
var cheese = new THREE.Mesh(cheeseGeometry, cheeseMaterial); // Changed const to var so it could be set to null in order to get rid of after collecting
cheese.position.set(0, 0, 3)
scene.add(cheese);
cheese.geometry.computeBoundingBox(cheeseBox);
const cheeseHelper = new THREE.Box3Helper(cheeseBox, 0xffff00);
scene.add(cheeseHelper);
/* SuperCheese */
const superCheeseGeometry = new THREE.SphereGeometry(bigCollectibleRadius, 100, 100);
const superCheeseMaterial = new THREE.MeshToonMaterial({ color: 0xcb6600, emissive: 0x0000ff });
const superCheese = new THREE.Mesh(superCheeseGeometry, superCheeseMaterial);
superCheese.position.set(0, 0, 5)
scene.add(superCheese);
/* Outer wall generation */
const walls = [];
const wallGeometry = new THREE.BoxGeometry(1, 1, 1);
const wallMaterial = new THREE.MeshToonMaterial({ color: 0xAAAAAA });
//Yes I know the wall being split into blocks might not look good for now
//Please trust me, it will make more sense later
//Top
for (var i = 0; i < 49; i++) {
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(24.5 - i, 0.5, 24.5)
walls.push(wall);
}
//Right
for (var i = 0; i < 49; i++) {
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(-24.5, 0.5, 24.5 - i)
walls.push(wall);
}
//Bottom
for (var i = 0; i < 49; i++) {
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(-24.5 + i, 0.5, -24.5)
walls.push(wall);
}
//Left
for (var i = 0; i < 49; i++) {
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(24.5, 0.5, -24.5 + i)
walls.push(wall);
}
//Tried to do the four of them as a single double loop
//Might try again later
for (var i = 0; i < walls.length; i++) scene.add(walls[i]);
//////////////////////////
function makeCoins(num, cheeseList_t = cheeseList, cheeseBoxList_t = cheeseBoxList) { //I know these names break the convention.
const smallCollectibleRadius = .4
const bigCollectibleRadius = .6
for (let i = 0; i < num; i++) {
console.log("I'm a fake cheese!")
const cheeseBox = new THREE.Box3();
const cheeseGeometry = new THREE.SphereGeometry(smallCollectibleRadius, 100, 100);
const cheeseMaterial = new THREE.MeshToonMaterial({ color: 0xffff00, emissive: 0xffff00 });
var cheese = new THREE.Mesh(cheeseGeometry, cheeseMaterial);
cheese.position.set(0, 0, (6 + i))
cheese.userData.idd = i // it's in a dict form, {"idd":i}
scene.add(cheese);
cheese.geometry.computeBoundingBox(cheeseBox);
const cheeseHelper = new THREE.Box3Helper(cheeseBox, 0xffff00);
scene.add(cheeseHelper);
cheeseList_t.push(cheese)
cheeseBoxList_t.push(cheeseBox)
}
}
makeCoins(2) // IT WORKS, HOLY COW
console.log(cheeseList[1])
function checkCollision(box) {
/* Code sucks but works and will by changed in future anyway */
var collision = playerBox.intersectsBox(box);
if (collision == true) {
return true
}
}
function collect(item) {
/* TODO: Collecting cheeses */
}
var points = 0;
function updatePoints(amount) {
points += amount
}
document.addEventListener("keypress", function (event) {
/* Additional player movement's listener that doesn't throw 2137 msgs in the console.
It would be great to make it the only one player movement's listener */
if (checkCollision(enemy1Box)) {
/**/
console.log("You bumped into enemy1!")
}
if (cheese !== null && !('consumed' in cheese.userData) && checkCollision(cheeseBox)) {
/* This will go to collect() function once finished */
console.log("Yummy cheese!")
scene.remove(cheese)
cheese.userData.consumed = true // good to know there is this userData feature
cheese = null
updatePoints(1)
}
/* For testing purposes */
var coinBox = cheeseBoxList[0]
if (checkCollision(coinBox)){
console.log("HOLY GUACAMOLE, IT WORKS") // IT REALLY DOES!
}
});
/////////////////////////////////////////////////////////////////////////////////////////////////
/* Render stuff */
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
const speed = 0.0005
const rotSpeed = 0.00005
const dir = new THREE.Vector3();
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
/* Updating boxes */
enemy1Box.copy(enemy1.geometry.boundingBox).applyMatrix4(enemy1.matrixWorld);
playerBox.copy(player.geometry.boundingBox).applyMatrix4(player.matrixWorld);
if (cheese !== null && !('consumed' in cheese.userData)) {
cheeseBox.copy(cheese.geometry.boundingBox).applyMatrix4(cheese.matrixWorld);
}
/* TEST */
var coin = cheeseList[0]
var coinBox = cheeseBoxList[0]
coinBox.copy(coin.geometry.boundingBox).applyMatrix4(coin.matrixWorld); // HOLY COW, IT SEEMS TO WORK
/* I'm afraid it'll lag everything but I can't think of better way of doing this;
this MUST be in render(), otherwise it won't update, but for loop in HERE... ugh
I will not run this. I care for my new PC. I don't want it to burn. I don't care for yours tho. Sorry not sorry, mate
IT'S NOT FINISHED YET, DON'T THINK OF RUNNING THIS
for (let i = 0; i < num; i++) {
coin = coinList[i]
coinBox = coinBoxList[i]
coinBox.copy(coin.geometry.boundingBox).applyMatrix4(coin.matrixWorld);
}
*/
document.getElementById("points").innerHTML = points
document.addEventListener("keypress", function (event) {
if (event.keyCode == 119) { //W going forward
if (checkCollision(enemy1Box)) {
console.log("Can't go any further!") // TODO: Delete in the future
} else {
player.getWorldDirection(dir);
player.position.addScaledVector(dir, speed);
}
}
if (event.keyCode == 115) { //S going backward
player.getWorldDirection(dir);
player.position.addScaledVector(dir, -speed);
}
if (event.keyCode == 97) { //A left rotation
player.rotation.y += rotSpeed
}
if (event.keyCode == 100) { //D right rotation
player.rotation.y -= rotSpeed
}
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
You’re adding keypress event listeners inside your render loop. This means that by the time 60 frames have gone by, you’ll have 60 event listeners doing the same thing. That’s why you’re seeing your scene speeding up: the more frames have passed, the more event listeners you’ve added.
Just initialize those events outside the render loop, and you’ll see the issue go away.
Related
Goal
To get this mini-game working in next.js powered environment 'the right way'.
Background
The linked game uses Three.js which in turn requires the window object to update certain variables according to the game logic.
And next.js does not have a window object defined on the server side, as explained here.
So, to tackle this problem, I moved the window object inside of useEffect hook provided by react as mentioned in this article.
Problem
Moving window inside of useEffect worked but other helper variables depends on its value, so to make it all work without errors, I had to move the entire javascript code with some minor adjustments inside of useEffect of a component page that I created.
Extra
I started this project by trying to recreate this game using react-three-fiber as it abstracts well with Next.js but I had to change my ways because the documentation was not clear and lacks example code.
To even change the default camera setting from perspective to orthographic inside of Canvas tag was a little unruly.
But as explained by Bruno Simon, Paul Henschel and others, three becomes quite hard to manage/scale/change/reuse when the code becomes complex.
And I wish to maintain the component property of my code.
How shall I go about it?
Thankyou for any leads, references to courses, articles.
This stackoverflow thread poses a similar question to mine but I have my what ifs.
Code
import styles from '../styles/Home.module.css'
import { React, useState, useLayoutEffect, useRef, useEffect } from 'react'
import * as THREE from 'three'
import * as CANNON from 'cannon'
export default function Home() {
useEffect(() => {
window.focus(); // Capture keys right away (by default focus is on editor)
let camera, scene, renderer; // ThreeJS globals
let world; // CannonJs world
let lastTime; // Last timestamp of animation
let stack; // Parts that stay solid on top of each other
let overhangs; // Overhanging parts that fall down
const boxHeight = 1; // Height of each layer
const originalBoxSize = 3; // Original width and height of a box
let autopilot;
let gameEnded;
let robotPrecision; // Determines how precise the game is on autopilot
const scoreElement = document.getElementById("score");
const instructionsElement = document.getElementById("instructions");
const resultsElement = document.getElementById("results");
init();
// Determines how precise the game is on autopilot
function setRobotPrecision() {
robotPrecision = Math.random() * 1 - 0.5;
}
function init() {
autopilot = true;
gameEnded = false;
lastTime = 0;
stack = [];
overhangs = [];
setRobotPrecision();
// Initialize CannonJS
world = new CANNON.World();
world.gravity.set(0, -10, 0); // Gravity pulls things down
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 40;
// Initialize ThreeJs
const aspect = window.innerWidth / window.innerHeight;
const width = 10;
const height = width / aspect;
camera = new THREE.OrthographicCamera(
width / -2, // left
width / 2, // right
height / 2, // top
height / -2, // bottom
0, // near plane
100 // far plane
);
/*
// If you want to use perspective camera instead, uncomment these lines
camera = new THREE.PerspectiveCamera(
45, // field of view
aspect, // aspect ratio
1, // near plane
100 // far plane
);
*/
camera.position.set(4, 4, 4);
camera.lookAt(0, 0, 0);
scene = new THREE.Scene();
// Foundation
addLayer(0, 0, originalBoxSize, originalBoxSize);
// First layer
addLayer(-10, 0, originalBoxSize, originalBoxSize, "x");
// Set up lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.6);
dirLight.position.set(10, 20, 0);
scene.add(dirLight);
// Set up renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(animation);
document.body.appendChild(renderer.domElement);
}
function startGame() {
autopilot = false;
gameEnded = false;
lastTime = 0;
stack = [];
overhangs = [];
if (instructionsElement) instructionsElement.style.display = "none";
if (resultsElement) resultsElement.style.display = "none";
if (scoreElement) scoreElement.innerText = 0;
if (world) {
// Remove every object from world
while (world.bodies.length > 0) {
world.remove(world.bodies[0]);
}
}
if (scene) {
// Remove every Mesh from the scene
while (scene.children.find((c) => c.type == "Mesh")) {
const mesh = scene.children.find((c) => c.type == "Mesh");
scene.remove(mesh);
}
// Foundation
addLayer(0, 0, originalBoxSize, originalBoxSize);
// First layer
addLayer(-10, 0, originalBoxSize, originalBoxSize, "x");
}
if (camera) {
// Reset camera positions
camera.position.set(4, 4, 4);
camera.lookAt(0, 0, 0);
}
}
function addLayer(x, z, width, depth, direction) {
const y = boxHeight * stack.length; // Add the new box one layer higher
const layer = generateBox(x, y, z, width, depth, false);
layer.direction = direction;
stack.push(layer);
}
function addOverhang(x, z, width, depth) {
const y = boxHeight * (stack.length - 1); // Add the new box one the same layer
const overhang = generateBox(x, y, z, width, depth, true);
overhangs.push(overhang);
}
function generateBox(x, y, z, width, depth, falls) {
// ThreeJS
const geometry = new THREE.BoxGeometry(width, boxHeight, depth);
const color = new THREE.Color(`hsl(${30 + stack.length * 4}, 100%, 50%)`);
const material = new THREE.MeshLambertMaterial({ color });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(x, y, z);
scene.add(mesh);
// CannonJS
const shape = new CANNON.Box(
new CANNON.Vec3(width / 2, boxHeight / 2, depth / 2)
);
let mass = falls ? 5 : 0; // If it shouldn't fall then setting the mass to zero will keep it stationary
mass *= width / originalBoxSize; // Reduce mass proportionately by size
mass *= depth / originalBoxSize; // Reduce mass proportionately by size
const body = new CANNON.Body({ mass, shape });
body.position.set(x, y, z);
world.addBody(body);
return {
threejs: mesh,
cannonjs: body,
width,
depth
};
}
function cutBox(topLayer, overlap, size, delta) {
const direction = topLayer.direction;
const newWidth = direction == "x" ? overlap : topLayer.width;
const newDepth = direction == "z" ? overlap : topLayer.depth;
// Update metadata
topLayer.width = newWidth;
topLayer.depth = newDepth;
// Update ThreeJS model
topLayer.threejs.scale[direction] = overlap / size;
topLayer.threejs.position[direction] -= delta / 2;
// Update CannonJS model
topLayer.cannonjs.position[direction] -= delta / 2;
// Replace shape to a smaller one (in CannonJS you can't simply just scale a shape)
const shape = new CANNON.Box(
new CANNON.Vec3(newWidth / 2, boxHeight / 2, newDepth / 2)
);
topLayer.cannonjs.shapes = [];
topLayer.cannonjs.addShape(shape);
}
window.addEventListener("mousedown", eventHandler);
window.addEventListener("touchstart", eventHandler);
window.addEventListener("keydown", function (event) {
if (event.key == " ") {
event.preventDefault();
eventHandler();
return;
}
if (event.key == "R" || event.key == "r") {
event.preventDefault();
startGame();
return;
}
});
function eventHandler() {
if (autopilot) startGame();
else splitBlockAndAddNextOneIfOverlaps();
}
function splitBlockAndAddNextOneIfOverlaps() {
if (gameEnded) return;
const topLayer = stack[stack.length - 1];
const previousLayer = stack[stack.length - 2];
const direction = topLayer.direction;
const size = direction == "x" ? topLayer.width : topLayer.depth;
const delta =
topLayer.threejs.position[direction] -
previousLayer.threejs.position[direction];
const overhangSize = Math.abs(delta);
const overlap = size - overhangSize;
if (overlap > 0) {
cutBox(topLayer, overlap, size, delta);
// Overhang
const overhangShift = (overlap / 2 + overhangSize / 2) * Math.sign(delta);
const overhangX =
direction == "x"
? topLayer.threejs.position.x + overhangShift
: topLayer.threejs.position.x;
const overhangZ =
direction == "z"
? topLayer.threejs.position.z + overhangShift
: topLayer.threejs.position.z;
const overhangWidth = direction == "x" ? overhangSize : topLayer.width;
const overhangDepth = direction == "z" ? overhangSize : topLayer.depth;
addOverhang(overhangX, overhangZ, overhangWidth, overhangDepth);
// Next layer
const nextX = direction == "x" ? topLayer.threejs.position.x : -10;
const nextZ = direction == "z" ? topLayer.threejs.position.z : -10;
const newWidth = topLayer.width; // New layer has the same size as the cut top layer
const newDepth = topLayer.depth; // New layer has the same size as the cut top layer
const nextDirection = direction == "x" ? "z" : "x";
if (scoreElement) scoreElement.innerText = stack.length - 1;
addLayer(nextX, nextZ, newWidth, newDepth, nextDirection);
} else {
missedTheSpot();
}
}
function missedTheSpot() {
const topLayer = stack[stack.length - 1];
// Turn to top layer into an overhang and let it fall down
addOverhang(
topLayer.threejs.position.x,
topLayer.threejs.position.z,
topLayer.width,
topLayer.depth
);
world.remove(topLayer.cannonjs);
scene.remove(topLayer.threejs);
gameEnded = true;
if (resultsElement && !autopilot) resultsElement.style.display = "flex";
}
function animation(time) {
if (lastTime) {
const timePassed = time - lastTime;
const speed = 0.008;
const topLayer = stack[stack.length - 1];
const previousLayer = stack[stack.length - 2];
// The top level box should move if the game has not ended AND
// it's either NOT in autopilot or it is in autopilot and the box did not yet reach the robot position
const boxShouldMove =
!gameEnded &&
(!autopilot ||
(autopilot &&
topLayer.threejs.position[topLayer.direction] <
previousLayer.threejs.position[topLayer.direction] +
robotPrecision));
if (boxShouldMove) {
// Keep the position visible on UI and the position in the model in sync
topLayer.threejs.position[topLayer.direction] += speed * timePassed;
topLayer.cannonjs.position[topLayer.direction] += speed * timePassed;
// If the box went beyond the stack then show up the fail screen
if (topLayer.threejs.position[topLayer.direction] > 10) {
missedTheSpot();
}
} else {
// If it shouldn't move then is it because the autopilot reached the correct position?
// Because if so then next level is coming
if (autopilot) {
splitBlockAndAddNextOneIfOverlaps();
setRobotPrecision();
}
}
// 4 is the initial camera height
if (camera.position.y < boxHeight * (stack.length - 2) + 4) {
camera.position.y += speed * timePassed;
}
updatePhysics(timePassed);
renderer.render(scene, camera);
}
lastTime = time;
}
function updatePhysics(timePassed) {
world.step(timePassed / 1000); // Step the physics world
// Copy coordinates from Cannon.js to Three.js
overhangs.forEach((element) => {
element.threejs.position.copy(element.cannonjs.position);
element.threejs.quaternion.copy(element.cannonjs.quaternion);
});
}
window.addEventListener("resize", () => {
// Adjust camera
console.log("resize", window.innerWidth, window.innerHeight);
const aspect = window.innerWidth / window.innerHeight;
const width = 10;
const height = width / aspect;
camera.top = height / 2;
camera.bottom = height / -2;
// Reset renderer
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.render(scene, camera);
});
})
return (
<div className={styles.container}>
<div className={styles.nav}>
<h2>Ritik Jangir</h2>
</div>
</div>
)
}
I'm still a beginner coder and I'm making a project where the eye positions (networked by socket) of two clients when landing on the same positions on the canvas would play the music note it lands on. I'm still in the beginning stages of this project and currently, I'm trying to draw the client's eye position on the canvas while the grid of music notes on a p5 renderer object. I had coded the grid to add an ellipse to where the mouse has clicked. The grid is successfully drawn but I no longer can interact with the grid i.e add or remove ellipses on click.
So now I'm a bit lost on how to solve this issue. Before when the eye was being drawn on p5.renderer I also tried the clear() function to get rid of the trails but it didn't work.
So I have two problems 1) trying to get rid of eye position trails and 2) using create graphics with mouse pressed function not working in code below.
// NETWORKED + EYE VARIABLES
let socket = io();
let ctracker;
let clients = {};
let data = {};
let w = 1200;
let h = 600;
let leftEyeX, leftEyeY, rightEyeX, rightEyeY;
let cnvPosX;
let cnvPosY;
/// SOUND VARIABLES //
let bell, bell1, bell2, bell3; // contains sound source
let bPat, b1Pat, b2Pat; // an array of no.s that we can use to make beats // 1 = on, 0= off
let bPhrase, b1Phrase, b2Phrase; // defines how the beat pattern is interpreted
let part; // attach all the instruments to the to make it into a machine i.e on/off
let bpmCTRL;
let beatLength; // how big is sequence before it starts looping
let cellSize;
let cnv;
let overlayCnv;
let bg, largeText,smallText, MleftEye, MRightEye;
let row1 =[];
function preload() {
// background
bg = loadImage("https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2Fdaw-bg.png?v=1589319965142");
//main game instruction
largeText = loadImage("https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2FAsset%2015.png?v=1589381930278");
// small game description
smallText = loadImage("https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2FAsset%203.png?v=1589381926354");
//sound files
bell = loadSound (
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203490__tesabob2001__g-5.mp3?v=1589326854551",
() => {}
);
bell1 = loadSound (
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203485__tesabob2001__c5.mp3?v=1589326924689",
() => {}
);
bell2 = loadSound (
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203489__tesabob2001__f5.mp3?v=1589326917439",
() => {}
);
bell3 = loadSound (
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203491__tesabob2001__g-4.mp3?v=1589326867294", () => {}
);
};
function setup() {
cnvPosX = 120;
cnvPosY = 50;
// setup camera capture
var videoInput = createCapture(VIDEO);
videoInput.size(w,h);
videoInput.position(cnvPosX, cnvPosY);
videoInput.hide();
// setup canvas
ctracker = new clm.tracker();
ctracker.init(pModel);
ctracker.start(videoInput.elt);
noStroke();
socket.on('getUpdate', function(data){
clients[data.name] = data;
});
cnv = createCanvas(w, h);
cnv.position(cnvPosX,cnvPosY)
overlayCnv = createGraphics(w,h);
// overlayCnv.position(cnvPosX,cnvPosY);
overlayCnv.mousePressed(canvasPressed);
beatLength = 6;
cellSize = 200;
numOfRows = 3;
// canvas for eyes
// basic structure of a DAW
// time is a sceduled delay in note play time
bPat = [0, 0, 0, 0, 0, 0];
b1Pat = [0, 0, 0, 0, 0, 0];
b2Pat = [0, 0, 0, 0, 0, 0];
function selectSong() {
row1 = [bell3, bell];
selected = row1[floor(random(2))];
console.log(selected);
selected.play();
}
// name, callback, array
bPhrase = new p5.Phrase(
"bell",
time => {
selectSong()
},
bPat
);
b1Phrase = new p5.Phrase(
"bell1",
time => {
// make a variable to go there insiead of bell -> use random function to give a value to the variable
bell1.play(time); },
b1Pat
);
b2Phrase = new p5.Phrase(
"bell2",
time => {
bell2.play(time);
},
b2Pat
);
part = new p5.Part();
part.addPhrase(bPhrase);
part.addPhrase(b1Phrase);
part.addPhrase(b2Phrase);
bpmCTRL = createSlider(30, 200, 60, 1); // smallest val, highest val, starting val, incremental val
bpmCTRL.position(10, 10); // x, y
bpmCTRL.input(() => {
part.setBPM(bpmCTRL.value());
});
part.setBPM("60");
drawMatrix();
///// user interact
function canvasPressed() {
console.log("mousepressed")
let rowClicked = floor(numOfRows * (mouseY / height));
let columnClicked = floor(beatLength * (mouseX / width));
if (rowClicked === 0) {
console.log("first row");
bPat[columnClicked] = +!bPat[columnClicked];
} else if (rowClicked === 1) {
console.log("second row");
b1Pat[columnClicked] = +!b1Pat[columnClicked];
} else if (rowClicked === 2) {
console.log("third row");
b2Pat[columnClicked] = +!b2Pat[columnClicked];
}
drawMatrix();
}
/// drawing the grid
function drawMatrix() {
overlayCnv.background(bg);
//line
overlayCnv.stroke(25,40,100);
overlayCnv.strokeWeight(2);
for (let i = 0; i < beatLength + 1; i++) {
overlayCnv.line(i * cellSize, 0, i * cellSize, height);
}
for (let i = 0; i < numOfRows + 1; i++) {
overlayCnv.line(0, (i * height) / numOfRows, width, (i * height) / numOfRows);
}
//circle
overlayCnv.noStroke();
overlayCnv.fill(25,40,100);
for (let i = 0; i < beatLength; i++) {
if (bPat[i] === 1) {
overlayCnv.ellipse(i * cellSize + 0.5 * cellSize, 100, 50);
}
if (b1Pat[i] === 1) {
overlayCnv.ellipse(i * cellSize + 0.5 * cellSize, 300, 40);
}
if (b2Pat[i] === 1) {
overlayCnv.ellipse(i * cellSize + 0.5 * cellSize, 500, 30);
}
}
}
image(overlayCnv, 0, 0);
}
function draw() {
let positions = ctracker.getCurrentPosition();
for (let i = 0; i < positions.length; i++) {
// draw ellipse at each position point
leftEyeX = positions[27][0];
leftEyeY = positions[27][1];
rightEyeX = positions[32][0];
rightEyeY = positions[32][1];
// ellipse(positions[i][0], positions[i][1], 8, 8);
fill(255);
ellipse(rightEyeX, rightEyeY, 18, 18);
fill(255);
ellipse(leftEyeX, leftEyeY, 18, 18);
// formatting each point into a dict entry to send over socket io
data[(27).toString() + "." + '0'] = leftEyeX;
data[(27).toString() + "." + '1'] = leftEyeY;
data[(32).toString() + "." + '0'] = rightEyeX;
data[(32).toString() + "." + '1'] = rightEyeY;
// image(eyeCnv,0,0); // x, y relative to canvas x & y
}
}
/////// conditional to check if all files are loaded
function keyPressed() {
if (key === " ") {
if (bell.isLoaded() && bell1.isLoaded() && bell2.isLoaded()) {
if (!part.isPlaying) {
part.loop();
} else {
part.stop();
}
} else {
console.log("relax");
}
}
}
Nevermind, the error was just that background kept drawing in draw()
I’m running into some errors. I want the user to select an eye player through clicking a button (either left eye or right eye) and then draw to the canvas only the selected input. I am using an eye tracker to draw some png files onto the canvas. I want them to draw only the left eye one if the user clicks on the left button.
I'm sorry for the extremely long code, I'm very new to code and don't know how to do it efficiently. The main variables I've been using for my problem is 'left' which is a button I created in setup() and in draw() I go through an array of points (clm eye tracker) and then save the left and right eye positions to objects, I then call the function mouse pressed on the left button.
// NETWORKED + EYE VARIABLES
let socket = io(); // new socket on client side
let ctracker; // eye tracker obj
let clients = {}; // client object
let data = {}; // data object
let w = 1200; // matrix width
let h = 600; // matrix height
let leftEye, rightEye; // main eye image
let SleftEye, SrightEye; // shadow eye image
let leftEyeX, leftEyeY, rightEyeX, rightEyeY; // current user eye data point vars
let cnvPosX, cnvPosY; // postion of main canvas
let playerLeft = {};
let playerRight = {};
let left,right;
/// SOUND VARIABLES //
let bell, bell1, bell2, bell3; // contains sound source
let bPat, b1Pat, b2Pat; // an array of nums that we can use to make a sequence of beats // 1 = on, 0= off
let bPhrase, b1Phrase, b2Phrase; // defines how the beat pattern is interpreted
let part; // attach all the above (sound,array,phrase) into a machine (part) i.e on/off,looper
let beatLength; // how big is sequence before it starts looping
let cellSize; // each sqauare size contains beat
let row1 = []; // array of sounds in row 1
// DOM variables
let play; // play button
let bg, largeText, smallText; // image variables for game instructions and background
let cnv; // main canvas
let matrixCnv; // canvas that draws the matrix ( music user interface )
let instructCnv; // instruction canvas at the topmost part
let bpmCTRL; // beat per minute slider
// let previousCo = {
// left: {row: null, column: null},
// right: {row: null, column:null}
// };
function preload() {
// background
bg = loadImage(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2Fdaw-bg.png?v=1589319965142"
);
//main game instruction
largeText = loadImage(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2FAsset%2015.png?v=1589381930278"
);
// small game description
smallText = loadImage(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2FAsset%203.png?v=1589381926354"
);
// game main player left eye
leftEye = loadImage(
"https://cdn.glitch.com/7e3760d0-fed0-482e-a757-d1ca0280067e%2Fleft-eye.png?v=1589284305903"
);
// game main player right eye
rightEye = loadImage(
"https://cdn.glitch.com/7e3760d0-fed0-482e-a757-d1ca0280067e%2Fright-eye.png?v=1589284306011"
);
// game shadow player left eye
SleftEye = loadImage(
"https://cdn.glitch.com/7e3760d0-fed0-482e-a757-d1ca0280067e%2Fother-left-eye.png?v=1589284306106"
);
// game shadow player left eye
SrightEye = loadImage(
"https://cdn.glitch.com/7e3760d0-fed0-482e-a757-d1ca0280067e%2Fother-right-eye.png?v=1589284332165"
);
//sound files
bell = loadSound(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203490__tesabob2001__g-5.mp3?v=1589326854551",
() => {}
);
bell1 = loadSound(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203485__tesabob2001__c5.mp3?v=1589326924689",
() => {}
);
bell2 = loadSound(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203489__tesabob2001__f5.mp3?v=1589326917439",
() => {}
);
bell3 = loadSound(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203491__tesabob2001__g-4.mp3?v=1589326867294",
() => {}
);
}
function setup() {
cnvPosX = 120;
cnvPosY = 50;
// setup camera capture
var videoInput = createCapture(VIDEO);
videoInput.size(w, h);
videoInput.position(cnvPosX, cnvPosY);
videoInput.hide();
// setup tracker
ctracker = new clm.tracker();
ctracker.init(pModel); // feed it model pca 20
ctracker.start(videoInput.elt); // feed it video data
// open socket io for connections
socket.on("getUpdate", function(data) {
clients[data.name] = data;
});
instructCnv = createGraphics(w, h);
instructCnv.background(0);
matrixCnv = createGraphics(w, h); // matrix canvas of daw
cnv = createCanvas(w, h); // main canvas that the eys are being drawn on
cnv.position(cnvPosX, cnvPosY); // main. canvas position
matrixCnv.mousePressed(canvasPressed); //drawMatrix() function on matrix function
cnv.mousePressed(canvasPressed); // drawMatrix() function on main function
beatLength = 6; // number of beats it loops thru && columns of matrix
cellSize = 200; // size of square
numOfRows = 3; // rows of matrix
play = createButton('Play/Pause');
play.position(1300, 10);
play.size(80,30);
play.mousePressed(playAudio);
// basic structure of a Digital Audio Workstation
// time is a sceduled delay in note play time
bPat = [0, 1, 0, 0, 0, 0];
b1Pat = [0, 0, 1, 0, 1, 1];
b2Pat = [1, 0, 0, 1, 0, 0];
// random selection of sound(bell3 or bell) in row 1
function selectSong() {
row1 = [bell3, bell];
selected = row1[floor(random(2))];
selected.setVolume(0.1);
selected.play();
}
// name, callback, array
bPhrase = new p5.Phrase(
"bell",
time => {
selectSong();
},
bPat
);
b1Phrase = new p5.Phrase(
"bell1",
time => {
// make a variable to go there insiead of bell -> use random function to give a value to the variable
bell1.setVolume(0.1);
bell1.play(time);
},
b1Pat
);
b2Phrase = new p5.Phrase(
"bell2",
time => {
bell2.setVolume(0.1);
bell2.play(time);
},
b2Pat
);
// creating a new part that contains all the above
part = new p5.Part();
part.addPhrase(bPhrase);
part.addPhrase(b1Phrase);
part.addPhrase(b2Phrase);
bpmCTRL = createSlider(30, 200, 60, 1); // smallest val, highest val, starting val, incremental val
bpmCTRL.position(10, 10); // x, y
bpmCTRL.input(() => {
part.setBPM(bpmCTRL.value());
});
part.setBPM("60");
//button left
left = createButton('Left');
left.position(300,15);
// drawing the grid
drawMatrix();
}
function draw() {
image(matrixCnv, 0, 0);
// background(bg);
let positions = ctracker.getCurrentPosition();
for (let i = 0; i < positions.length; i++) {
// find in array each position point
playerLeft = {
x : positions[27][0],
y : positions[27][1]
playerRight = {
x : positions[32][0],
y : positions[32][1]
}
left.mousePressed(drawLeft(playerLeft.x, playerLeft.y));
// formatting each point into a dict entry to send over socket io
data[(27).toString() + "." + "0"] = playerLeft.x;
data[(27).toString() + "." + "1"] = playerLeft.y;
data[(32).toString() + "." + "0"] = playerRight.x;
data[(32).toString() + "." + "1"] = playerRight.y;
// update client details
data["name"] = socket.id;
data["updated"] = new Date().getTime();
socket.emit("update", data);
//recieving data by other client and drawing
var x, y;
for (var c in clients) {
if (c == socket.id) {
// if client is not the the current user
continue;
}
var points = clients[c];
// console.log(points)
var leftEyePoint = {
x: points['27.0'],
y: points['27.1']
};
var rightEyePoint = {
x: points['32.0'],
y: points['32.1']
};
image(SleftEye, leftEyePoint.x, leftEyePoint.y, 150, 100)
image(SrightEye, rightEyePoint.x, rightEyePoint.y, 150, 100)
// canvasEyed(leftEyePoint, rightEyePoint)
}
}
drawMatrix();
}
function drawLeft(x,y) {
push();
translate(x, y);
image(leftEye, 0, 0, 150, 100);
pop();
}
function drawRight() {
push();
translate(playerRight.x, playerRight.y);
image(rightEye, 0, 0, 150, 100);
pop();
}
/////// conditional to check if all files are loaded
function playAudio() {
if (bell.isLoaded() && bell1.isLoaded() && bell2.isLoaded()) {
if (!part.isPlaying) {
part.loop();
} else {
part.stop();
}
} else {
console.log("relax and wait for sound to load");
}
}
/// user interact
function canvasPressed() {
// console.log("mousepressed")
let rowClicked = floor(numOfRows * (mouseY / height));
let columnClicked = floor(beatLength * (mouseX / width));
if (rowClicked === 0) {
console.log("first row");
bPat[columnClicked] = +!bPat[columnClicked];
} else if (rowClicked === 1) {
console.log("second row");
b1Pat[columnClicked] = +!b1Pat[columnClicked];
} else if (rowClicked === 2) {
console.log("third row");
b2Pat[columnClicked] = +!b2Pat[columnClicked];
}
}
/// drawing the grid
function drawMatrix() {
matrixCnv.background(bg);
//line
matrixCnv.stroke(25, 40, 100);
matrixCnv.strokeWeight(2);
for (let i = 0; i < beatLength + 1; i++) {
matrixCnv.line(i * cellSize, 0, i * cellSize, height);
}
for (let i = 0; i < numOfRows + 1; i++) {
matrixCnv.line(
0,
(i * height) / numOfRows,
width,
(i * height) / numOfRows
);
}
//circle
matrixCnv.noStroke();
matrixCnv.fill(25, 40, 100);
for (let i = 0; i < beatLength; i++) {
if (bPat[i] === 1) {
matrixCnv.ellipse(i * cellSize + 0.5 * cellSize, 100, 50);
}
if (b1Pat[i] === 1) {
matrixCnv.ellipse(i * cellSize + 0.5 * cellSize, 300, 40);
}
if (b2Pat[i] === 1) {
matrixCnv.ellipse(i * cellSize + 0.5 * cellSize, 500, 30);
}
}
}
Dan O is right,
That is not the correct way of passing variables as arguments in callback functions.
left.mousePressed(drawLeft(playerLeft.x, playerLeft.y));
It Should be:
left.mousePressed( function()=>{
drawLeft( playerLeft.x, playerLeft.y );
} );
This way you create a new function, an extra step inbetween. The function calls drawLeft(). Then pass that new function as the callback function. It's the same as doing this:
let afterMousePressed = function(){
drawLeft( playerLeft.x, playerLeft.y );
};
left.mousePressed( afterMousePressed );
what I try to accomplish sounds easy but bothers me for days now.
How may I add Objects to a scene, with a little pause between every adding ?
Within a loop{
I am calling the make_obj- function()
then I call the wait- function().
}
What happens is, that the program does not show the single added objects followed by a pause but puts them all together to the screen when the loop ends after all pauses were made.
I tried to explain it before here
Three.js scene not reacting while program is running
but it seems to be explained to complicated so I broke it down.
Thanks in advance.
Edit_1:
Yes, I can.
Please be so kind and think of scene and three as objects inserted by another class.
These are standard instantions
test = function ( scene, three ){
this.scene = scene;
this.caller_inst = three;
var that= this;
var depth_mod=0;
this.start_loop=function(){
var i;
for( i = 0;
i < 10;
i++
)
this.make_objects();
this.wait(1000);
};
this.wait = function( ms ){
// comment these 2 lines to see effect of "normal" wait loop
setTimeout(function(){ console.log("Wait!"); }, ms);
return;
console.log( "wait start");
var start = new Date ()
. getTime ();
var end = start;
while( end < start + ms ){
end = new Date ()
. getTime ();
}//while
console.log( "wait ended");
};
this.make_objects = function( count_rows, count_columns ){
var color = '#'+Math.floor(Math.random()*16777215).toString(16);
var sphere_geometry = new THREE.SphereGeometry(1);
var material1 = new THREE.MeshPhongMaterial({ color: color, specular: 0x555555 });
var sphere_mesh = new THREE.Mesh(sphere_geometry, material1);
this.scene.add( sphere_mesh );
sphere_mesh.position . copy ( that.caller_inst.camera.position );
sphere_mesh.position.x = sphere_mesh.position.x+5+depth_mod;
sphere_mesh.position.z = sphere_mesh.position.z-15 + depth_mod;
that.caller_inst.renderer . render ( that.caller_inst.scene
, that.caller_inst.camera
);
depth_mod = depth_mod-1;
};//function
};//class
I tried a little modification which brings the sam result.
this.start_loop=function(){
var i;
for( i = 0;
i < 10;
i++
){
setTimeout(function(){ that.make_objects(); }, 1500);
}
};
Why does a manual waiting not work like:
var i;
for( i = 0;
i < 10;
i++
)
this.object[ i ].visible = true;
this.wait(1000);
};
?
With setTimeout function.
Make a function that adds an object. Then just call that via setTimeout. For multiple additions, you can just add another setTimeout call at the end of your object adding function.
function sayHi() {
alert('Hello');
}
setTimeout(sayHi, 1000);
This page seems to have nice examples: https://javascript.info/settimeout-setinterval
There are lots of ways to do something in a loop with a pause. The modern es6 way is to use async functions.
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
async function doThingsOverTime() {
for (let i = 0; i < 10; ++i) {
console.log(i); // add object here
await wait(1000); // wait 1 second
}
}
doThingsOverTime();
example:
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r113/build/three.module.js';
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
async function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 50;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 16;
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
function makeInstance(geometry, color, x) {
const material = new THREE.MeshPhongMaterial({color});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cube.position.x = x;
return cube;
}
const cubes = [];
function render(time) {
time *= 0.001; // convert time to seconds
cubes.forEach((cube, ndx) => {
const speed = 1 + ndx * .1;
const rot = time * speed;
cube.rotation.x = rot;
cube.rotation.y = rot;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
const numCubes = 11;
for (let i = 0; i < numCubes; ++i) {
const u = i / (numCubes - 1);
const x = (u * 2 - 1) * 10;
cubes.push(makeInstance(geometry, `hsl(${u * 360},100%,70%)`, x));
await wait(1000);
}
}
main();
</script>
I have got the following code:
const linksGraphics = new PIXI.Graphics();
const update = () => {
linksGraphics.clear();
linksGraphics.alpha = 1;
if (forceLinkActive) {
data.links.forEach(link => {
let { source, target } = link;
linksGraphics.lineStyle(2, 0x000000);
linksGraphics.moveTo(source.x, source.y);
linksGraphics.lineTo(target.x, target.y);
});
linksGraphics.endFill();
} }
app.ticker.add( () => update() );
Where data.links is an array of edge data {source: number, target: number}. If I understand right, all lines are part of the PIXI.Graphics object. But what I need:
every line should have own opacity
every line should have an event for mouse over
Any ideas how modify my code?
Thanks.
It's been a while but can make a suggestion. Lines do not react to mouse/pointer over events in pixijs.
Instead you may want to accompany a transformed rectangle with alpha value 0 and listen mouse/pointer with this rectangle.
For example lets, change the alpha value of the line when mouse/pointer hovers the accompanying rectangle.
const app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x283230
});
document.body.appendChild(app.view);
// 1. PRELIMINARY COMPUTATIONS
// Coordinates of the end points of a line
let x0 = 100;
let y0 = 100;
let x1 = 200;
let y1 = 200;
// Find midpoint for translation
let xmid = 0.5*(x0+x1);
let ymid = 0.5*(y0+y1);
// Length of the line
let length = Math.hypot(x0-x1, y0-y1);
// Alignment angle of the line, i.e. angle with the x axis
let angle = Math.atan((y1-y0)/(x1-x0));
// 2. LINE
line = new PIXI.Graphics();
// Arbitrary line style, say we have a non-white background
line.lineStyle(8,0xffffff,1);
line.moveTo(x0,y0);
line.lineTo(x1,y1);
// 3. ACCOMPANYING RECTANGLE
line.rectangle = new PIXI.Graphics();
line.rectangle.beginFill(0xffffff);
// Since we are going to translate, think of 0,0 is the center point on the rectangle
// Width of the rectangle is selected arbitrarily as 30
const width = 30;
line.rectangle.drawRect(-length/2,-width/2,length,width);
line.rectangle.endFill();
line.rectangle.alpha = 0;
line.rectangle.interactive = true;
line.rectangle.on("pointerover", reactOver);
line.rectangle.on("pointerout", reactOut);
// Apply transformation
line.rectangle.setTransform(xmid, ymid,1,1,angle);
app.stage.addChild(line);
// Add rectangle to the stage too.
app.stage.addChild(line.rectangle);
// Let's change alpha value of the line when user hovers.
function reactOver(){
line.alpha = 0.5;
}
function reactOut(){
line.alpha = 1;
}
To the PEN, Hover a line in pixijs
We can expand this logic to a rectangle for instance. But this time you need two accompanying rectangles (with alpha=0) where one of them is wider and the other is narrower than the unfilled rectangle. For example,
const app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x283230
});
document.body.appendChild(app.view);
const x = 100;
const y = 100;
const width = 150;
const height = 100;
const hoverWidth = 20;
const rect = new PIXI.Graphics();
rect.lineStyle(4, 0xffffff,1);
rect.drawRect(x,y,width,height);
rect.outer = new PIXI.Graphics();
rect.inner = new PIXI.Graphics();
// Fill outer
rect.outer.alpha = 0;
rect.outer.beginFill(0xffffff);
rect.outer.drawRect(x-hoverWidth/2, y-hoverWidth/2, width+hoverWidth, height+hoverWidth);
rect.outer.endFill();
// Fill inner
rect.inner.alpha = 0;
rect.inner.beginFill(0xffffff);
rect.inner.drawRect(x+hoverWidth/2, y+hoverWidth/2, width-hoverWidth, height-hoverWidth);
rect.inner.endFill();
// Add interaction and listeners
rect.outer.interactive = true;
rect.inner.interactive = true;
rect.outer.on("pointerover", pOverOuter);
rect.outer.on("pointerout", pOutOuter);
rect.inner.interaction = true;
rect.inner.on("pointerover", pOverInner);
rect.inner.on("pointerout", pOutInner);
app.stage.addChild(rect);
app.stage.addChild(rect.outer);
app.stage.addChild(rect.inner);
// Listeners
let overOuter = false;
let overInner = false;
function pOverOuter(){
overOuter = true;
changeAlpha();
// rect.alpha = 0.5;
}
function pOutOuter(){
overOuter = false;
changeAlpha();
}
function pOverInner(){
overInner = true;
changeAlpha();
// rect.alpha = 1;
}
function pOutInner(){
overInner = false;
changeAlpha();
// rect.alpha = 0.5;
}
function changeAlpha(){
rect.alpha = (overOuter && !overInner)? 0.5: 1;
}
To the PEN, Hover a rectangle in pixijs
For your first requirement, try creating separate graphics objects for drawing each line and set alpha for each line.
For your second requirement, You need to set the interactive property of graphics (linksGraphics) object to true like below,
linksGraphics.interactive = true;
and then attach a function to be executed on mouseover event like below,
var mouseOverAction = function () {
//Some code
};
linksGraphics.on('mouseover', mouseOverAction);
You can define a hitArea on a graphic. And with getBounds() you can make a line clickable. After you do that you can also assign pointerEvents to the graphic.
const linksGraphics = new PIXI.Graphics();
const update = () => {
linksGraphics.clear();
linksGraphics.alpha = 1;
if (forceLinkActive) {
data.links.forEach(link => {
let { source, target } = link;
linksGraphics.lineStyle(2, 0x000000);
linksGraphics.moveTo(source.x, source.y);
linksGraphics.lineTo(target.x, target.y);
//A line itself is not clickable
linksGraphics.hitArea = linksGraphics.getBounds();
});
linksGraphics.endFill();
}
}
app.ticker.add( () => update() );