Three JS: OrbitControls the pixel of picture jumps when dragging - javascript

I recently started to deal with 3d, I want to move around the scene, but when dragging, I see how the pixels in the pictures start jumping before the camera stops moving. This is clearly the wrong job
Why is this happening? And how to avoid it?
My code: https://codepen.io/davedev13/pen/zYEyxRX
const data = {
"objects": [
{
"img": "https://picsum.photos/400/500",
"title": "Creative",
"description": "lorem ipsum",
"url": "/1",
"position": {
"left": "-500",
"top": "500"
}
},
{
"img": "https://picsum.photos/400/500",
"title": "Angles",
"description": "lorem ipsum",
"url": "/2",
"position": {
"left": "500",
"top": "-500"
}
},
{
"img": "https://picsum.photos/400/500",
"title": "Awwards",
"description": "lorem ipsum",
"url": "/3",
"position": {
"left": "500",
"top": "0"
}
},
{
"img": "https://picsum.photos/400/500",
"title": "Conexe",
"description": "lorem ipsum",
"url": "/4",
"position": {
"left": "0",
"top": "500"
}
},
{
"img": "https://picsum.photos/400/500",
"title": "Luxury",
"description": "lorem ipsum",
"url": "/5",
"position": {
"left": "-500",
"top": "0"
}
},
{
"img": "https://picsum.photos/400/500",
"title": "Develop",
"description": "lorem ipsum",
"url": "/6",
"position": {
"left": "0",
"top": "-500"
}
},
{
"img": "https://picsum.photos/400/500",
"title": "Desing",
"description": "lorem ipsum",
"url": "/7",
"position": {
"left": "500",
"top": "500"
}
},
{
"img": "https://picsum.photos/400/500",
"title": "Sociality",
"description": "lorem ipsum",
"url": "/8",
"position": {
"left": "-500",
"top": "-500"
}
}
]
}
class Drag {
drag = {
width: 2560,
height: 1440,
};
mouseOut = false;
constructor(container) {
this.container = container;
let w = container.clientWidth;
let h = container.clientHeight;
let viewSize = h;
let aspectRatio = w / h;
this.viewport = {
viewSize: viewSize,
aspectRatio: aspectRatio,
left: (-aspectRatio * viewSize) / 2,
right: (aspectRatio * viewSize) / 2,
top: viewSize / 2,
bottom: -viewSize / 2,
near: -10,
far: 100
}
this.initScene();
}
initScene() {
this.camera = new THREE.OrthographicCamera(
this.viewport.left, this.viewport.right,
this.viewport.top, this.viewport.bottom,
this.viewport.near, this.viewport.far
);
this.scene = new THREE.Scene();
this.renderer = new THREE.WebGLRenderer({
// alpha: true // чтобы сцена была прозрачной
});
this.renderer.domElement.id = 'canvasGrid';
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setClearColor(0xdcdcdc, 1);
this.container.appendChild(this.renderer.domElement);
// драг контейнер для сцены
const geometry = new THREE.PlaneGeometry(this.drag.width, this.drag.height);
const material = new THREE.MeshBasicMaterial({
color: 0xdcdcdc,
});
this.drag.plane = new THREE.Mesh(geometry, material);
// драг бокс по которому гранимац которого будет двигаться сцена при драге
this.dragBox = new THREE.Box3().setFromObject(this.drag.plane);
this.scene.add(this.drag.plane);
this.sceneObjects();
this.setControls();
this.animate();
window.addEventListener('mouseout', () => this.mouseOut = true, false);
window.addEventListener('mouseover', () => {
this.mouseOut = false;
this.animate();
}, false);
}
sceneObjects() {
// instantiate a loader
const loader = new THREE.TextureLoader();
data.objects.map((item) => {
const group = new THREE.Group();
group.name = item.title;
// load a resource
loader.load(
// resource URL
item.img,
// onLoad callback
function (texture) {
const width = texture.image.naturalWidth * 0.5;
const height = texture.image.naturalHeight * 0.5;
// in this example we create the material when the texture is loaded
const geometry = new THREE.BoxGeometry(width, height, 0);
const material = new THREE.MeshBasicMaterial({map: texture});
const mesh = new THREE.Mesh(geometry, material);
group.add(mesh);
group.position.set(item.position.left, item.position.top, 0);
},
// onProgress callback currently not supported
undefined,
// onError callback
function (err) {
console.error('An error happened.', err);
}
);
this.scene.add(group);
});
const geometry1 = new THREE.BoxGeometry(550, 300, 0);
const texture = new THREE.TextureLoader().load("https://miro.medium.com/max/1400/1*Ynit7J26tXLwyq-sB3AUug.png");
console.log(texture)
const material1 = new THREE.MeshBasicMaterial({map: texture, opacity: 0.3});
const mesh = new THREE.Mesh(geometry1, material1);
this.scene.add(mesh);
}
render() {
// if (this.mouseDownPressed) {
let x1 = this.camera.position.x + (this.camera.left / this.camera.zoom);
let x1a = Math.max(x1, this.dragBox.min.x);
let pos_x = x1a - (this.camera.left / this.camera.zoom);
let x2 = pos_x + (this.camera.right / this.camera.zoom);
let x2a = Math.min(x2, this.dragBox.max.x);
pos_x = x2a - (this.camera.right / this.camera.zoom);
let y1 = this.camera.position.y + (this.camera.bottom / this.camera.zoom);
let y1a = Math.max(y1, this.dragBox.min.y);
let pos_y = y1a - (this.camera.bottom / this.camera.zoom);
let y2 = pos_y + (this.camera.top / this.camera.zoom);
let y2a = Math.min(y2, this.dragBox.max.y);
pos_y = y2a - (this.camera.top / this.camera.zoom);
this.camera.position.set(pos_x, pos_y, this.camera.position.z);
this.camera.lookAt(pos_x, pos_y, this.controls.target.z); // todo: what is it?
this.controls.target.set(pos_x, pos_y, 0);
this.controls.update();
// }
this.renderer.render(this.scene, this.camera);
}
setControls() {
// Need to be similar to what is in OrbitControls3Dpane.js constructor
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.mouseButtons = {
LEFT: THREE.MOUSE.PAN,
MIDDLE: THREE.MOUSE.DOLLY,
RIGHT: THREE.MOUSE.PAN
}
//////////////////////////////////////
// Set rotate related parameters
//////////////////////////////////////
// No rotation.
this.controls.enableRotate = false;
this.controls.minPolarAngle = Math.PI / 2;
this.controls.maxPolarAngle = Math.PI / 2;
// No orbit horizontally.
this.controls.minAzimuthAngle = 0; // radians
this.controls.maxAzimuthAngle = 0; // radians
//////////////////////////////////////
// Set zoom related parameters
//////////////////////////////////////
this.controls.enableZoom = true;
this.controls.zoomSpeed = 0.9;
this.controls.minDistance = this.camera.near;
this.controls.maxDistance = this.camera.far;
this.controls.minZoom = window.innerWidth > window.innerHeight ?
window.innerWidth / this.drag.width : window.innerHeight / this.drag.height;
this.controls.maxZoom = 1 + this.controls.minZoom;
//////////////////////////////////////
// Set pan related parameters
//////////////////////////////////////
this.controls.enablePan = true;
this.controls.panSpeed = 0.6;
this.controls.screenSpacePanning = true;
this.controls.enableDamping = true;
// this.renderer.domElement.addEventListener('mousedown', () => {
// this.mouseDownPressed = true;
// }, false);
// this.renderer.domElement.addEventListener('mouseup', () => {
// setTimeout(() => {
// this.mouseDownPressed = false;
// }, 700);
// }, false);
}
animate() {
this.render();
if (!this.mouseOut) {
requestAnimationFrame(this.animate.bind(this));
}
}
}
function init() {
new Drag(document.querySelector('.canvas'));
}
init();
.canvas {
width: 100vw;
height: 100vh;
}
<!-- partial:index.partial.html -->
<div class="canvas"></div>
<!-- partial -->
<script src='https://threejs.org/build/three.js'></script>
<script src='https://threejs.org/examples/js/controls/OrbitControls.js'></script><script src="./script.js"></script>
I created a scene using "OrthographicCamera" and move this camera along with OrbitControls, perhaps this is not the best option in my case, if so - tell me how to do it correctly?

antialias: true resolved at WebGLRenderer
with all the same settings, the scene immediately became smoother and the pictures do not bug
that is, he lowered the performance and taxied out...

Related

How can I change text color and font of the text produced by my Javascript?

This is code for a spinning wheel. When a person clicks the spinning wheel, it rotates and gives a result after a few seconds (depending on the script's time settings). How can an edit the color of the below text? So, when the wheel stops, the result text font color must be red on black background. Which part of the code has the reference to this?
(function($) {
var venues = [{
"name": "1900 Mexican Grill",
"type": "Mexican"
}, {
"name": "300 East",
"type": "American/Upscale"
}, {
"name": "Angry Ale's",
"type": "American/Pub "
}, {
"name": "Azteca",
"type": "Mexican"
}, {
"name": "Bedder Bedder and Moore",
"type": "Sandwiches, Salads, Soups"
}, {
"name": "Boading",
"type": "Chinese"
}, {
"name": "Brazwell's Premium Pub",
"type": "American/Pub "
}, {
"name": "Brio Tuscan Grille",
"type": "Italian"
}, {
"name": "Brixx",
"type": "Pizza/Salads"
}, {
"name": "Café at 6100",
"type": "Home Cooking"
}, {
"name": "California Pizza Kitchen",
"type": "Pizza/Salads"
}, {
"name": "Chick-Fil-A",
"type": "Fast Food"
}, {
"name": "City Tavern",
"type": "American/Upscale"
}, {
"name": "Copper",
"type": "Indian"
}, {
"name": "Cowfish",
"type": "Sushi"
}, {
"name": "Duckworth's",
"type": "American/Pub "
}, {
"name": "Eddie's Place",
"type": "American/Pub "
}, {
"name": "El Camino",
"type": "Mexican"
}, {
"name": "Fairview Plaza Restaurant",
"type": "Home Cooking"
}, {
"name": "Firebird's Wood Fired Grill",
"type": "American/Upscale"
}, {
"name": "Firehouse Subs",
"type": "Sandwiches, Salads, Soups"
}, {
"name": "Flying Biscuit",
"type": "American/Fast"
}, {
"name": "Fuel",
"type": "Pizza"
}, {
"name": "Good Food on Montford",
"type": "American/Upscale"
}, {
"name": "Harper's Restaurant",
"type": "American/Upscale"
}, {
"name": "Hawthorne's Pizza",
"type": "Pizza/Italian"
}, {
"name": "Luisa's Brick Oven Pizza",
"type": "Pizza/Italian"
}, {
"name": "Maverick Rock Taco",
"type": "Mexican"
}, {
"name": "McAllister's",
"type": "Sandwiches, Salads, Soups"
}, {
"name": "Mellow Mushroom",
"type": "Pizza/Salads"
}, {
"name": "Moe's",
"type": "Mexican"
}, {
"name": "Moosehead Grill",
"type": "American/Pub "
}, {
"name": "Paco's Tacos and Tequila",
"type": "Mexican"
}, {
"name": "Panera ",
"type": "Sandwiches, Salads, Soups"
}, {
"name": "PF Chang's",
"type": "Chinese"
}, {
"name": "Portofinos",
"type": "Pizza/Italian"
}, {
"name": "Qdoba",
"type": "Mexican"
}, {
"name": "Rooster's Wood Fire Kitchen",
"type": "American/Upscale"
}, {
"name": "Rusty's Deli",
"type": "Sandwiches, Salads, Soups"
}, {
"name": "Taco Bell",
"type": "Fast Food"
}, {
"name": "Taco Mac",
"type": "American/Pub "
}, {
"name": "Terrace Café",
"type": "American/Upscale"
}, {
"name": "The Roasting Company",
"type": "American/Fast"
}, {
"name": "Village Tavern",
"type": "American/Upscale"
}, {
"name": "Which Witch?",
"type": "Sandwiches, Salads, Soups"
}, {
"name": "Zack's Hamburgers",
"type": "American/Fast"
}];
// Helpers
var blackHex = '#333',
whiteHex = '#fff',
shuffle = function(o) {
for (var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x)
;
return o;
},
halfPI = Math.PI / 2,
doublePI = Math.PI * 2;
String.prototype.hashCode = function() {
// See http://www.cse.yorku.ca/~oz/hash.html
var hash = 5381,
i;
for (i = 0; i < this.length; i++) {
char = this.charCodeAt(i);
hash = ((hash << 5) + hash) + char;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
};
Number.prototype.mod = function(n) {
return ((this % n) + n) % n;
};
// WHEEL!
var wheel = {
timerHandle: 0,
timerDelay: 33,
angleCurrent: 0,
angleDelta: 0,
size: 290,
canvasContext: null,
colors: ['#003366', '#FF6600', '#CCCC00', '#006600', '#3333CC', '#CC0066', '#FF3300', '#009900', '#6600CC', '#33CC33', '#0066CC', '#FF0066', '#3300FF', '#00CC00', '#FFCC00'],
segments: [],
seg_colors: [], // Cache of segments to colors
maxSpeed: Math.PI / 16,
upTime: 1000, // How long to spin up for (in ms)
downTime: 5000, // How long to slow down for (in ms)
spinStart: 0,
frames: 0,
centerX: 300,
centerY: 300,
spin: function() {
// Start the wheel only if it's not already spinning
if (wheel.timerHandle == 0) {
wheel.spinStart = new Date().getTime();
wheel.maxSpeed = Math.PI / (16 + Math.random()); // Randomly vary how hard the spin is
wheel.frames = 0;
wheel.sound.play();
wheel.timerHandle = setInterval(wheel.onTimerTick, wheel.timerDelay);
}
},
onTimerTick: function() {
var duration = (new Date().getTime() - wheel.spinStart),
progress = 0,
finished = false;
wheel.frames++;
wheel.draw();
if (duration < wheel.upTime) {
progress = duration / wheel.upTime;
wheel.angleDelta = wheel.maxSpeed *
Math.sin(progress * halfPI);
} else {
progress = duration / wheel.downTime;
wheel.angleDelta = wheel.maxSpeed *
Math.sin(progress * halfPI + halfPI);
if (progress >= 1) {
finished = true;
}
}
wheel.angleCurrent += wheel.angleDelta;
while (wheel.angleCurrent >= doublePI) {
// Keep the angle in a reasonable range
wheel.angleCurrent -= doublePI;
}
if (finished) {
clearInterval(wheel.timerHandle);
wheel.timerHandle = 0;
wheel.angleDelta = 0;
if (console) {
console.log((wheel.frames / duration * 1000) + " FPS");
}
}
/*
// Display RPM
var rpm = (wheel.angleDelta * (1000 / wheel.timerDelay) * 60) / (Math.PI * 2);
$("#counter").html( Math.round(rpm) + " RPM" );
*/
},
init: function(optionList) {
try {
wheel.initWheel();
wheel.initAudio();
wheel.initCanvas();
wheel.draw();
$.extend(wheel, optionList);
} catch (exceptionData) {
alert('Wheel is not loaded ' + exceptionData);
}
},
initAudio: function() {
var sound = document.createElement('audio');
sound.setAttribute('src', 'wheel.mp3');
wheel.sound = sound;
},
initCanvas: function() {
var canvas = $('#canvas')[0];
canvas.addEventListener("click", wheel.spin, false);
wheel.canvasContext = canvas.getContext("2d");
},
initWheel: function() {
shuffle(wheel.colors);
},
// Called when segments have changed
update: function() {
// Ensure we start mid way on a item
//var r = Math.floor(Math.random() * wheel.segments.length);
var r = 0,
segments = wheel.segments,
len = segments.length,
colors = wheel.colors,
colorLen = colors.length,
seg_color = [], // Generate a color cache (so we have consistant coloring)
i
wheel.angleCurrent = ((r + 0.5) / wheel.segments.length) * doublePI;
for (i = 0; i < len; i++) {
seg_color.push(colors[segments[i].hashCode().mod(colorLen)]);
}
wheel.seg_color = seg_color;
wheel.draw();
},
draw: function() {
wheel.clear();
wheel.drawWheel();
wheel.drawNeedle();
},
clear: function() {
wheel.canvasContext.clearRect(0, 0, 1000, 800);
},
drawNeedle: function() {
var ctx = wheel.canvasContext,
centerX = wheel.centerX,
centerY = wheel.centerY,
size = wheel.size,
i,
centerSize = centerX + size,
len = wheel.segments.length,
winner;
ctx.lineWidth = 2;
ctx.strokeStyle = blackHex;
ctx.fillStyle = whiteHex;
ctx.beginPath();
ctx.moveTo(centerSize - 10, centerY);
ctx.lineTo(centerSize + 10, centerY - 10);
ctx.lineTo(centerSize + 10, centerY + 10);
ctx.closePath();
ctx.stroke();
ctx.fill();
// Which segment is being pointed to?
i = len - Math.floor((wheel.angleCurrent / doublePI) * len) - 1;
// Now draw the winning name
ctx.textAlign = "left";
ctx.textBaseline = "middle";
ctx.fillStyle = blackHex;
ctx.font = "2em Arial";
winner = wheel.segments[i] || 'Choose at least 1 Venue';
ctx.fillText(winner, centerSize + 20, centerY);
},
drawSegment: function(key, lastAngle, angle) {
var ctx = wheel.canvasContext,
centerX = wheel.centerX,
centerY = wheel.centerY,
size = wheel.size,
colors = wheel.seg_color,
value = wheel.segments[key];
//ctx.save();
ctx.beginPath();
// Start in the centre
ctx.moveTo(centerX, centerY);
ctx.arc(centerX, centerY, size, lastAngle, angle, false); // Draw an arc around the edge
ctx.lineTo(centerX, centerY); // Now draw a line back to the center
// Clip anything that follows to this area
//ctx.clip(); // It would be best to clip, but we can double performance without it
ctx.closePath();
ctx.fillStyle = colors[key];
ctx.fill();
ctx.stroke();
// Now draw the text
ctx.save(); // The save ensures this works on Android devices
ctx.translate(centerX, centerY);
ctx.rotate((lastAngle + angle) / 2);
ctx.fillStyle = whiteHex;
ctx.fillText(value.substr(0, 20), size - 15, 0);
ctx.restore();
},
drawWheel: function() {
var ctx = wheel.canvasContext,
angleCurrent = wheel.angleCurrent,
lastAngle = angleCurrent,
len = wheel.segments.length,
centerX = wheel.centerX,
centerY = wheel.centerY,
size = wheel.size,
angle,
i;
ctx.lineWidth = 1;
ctx.strokeStyle = blackHex;
ctx.textBaseline = "middle";
ctx.textAlign = "right";
ctx.font = "1em Arial";
for (i = 1; i <= len; i++) {
angle = doublePI * (i / len) + angleCurrent;
wheel.drawSegment(i - 1, lastAngle, angle);
lastAngle = angle;
}
// Draw a center circle
ctx.beginPath();
ctx.arc(centerX, centerY, 20, 0, doublePI, false);
ctx.closePath();
ctx.fillStyle = whiteHex;
//ctx.strokeStyle = blackHex;
ctx.fill();
ctx.stroke();
// Draw outer circle
ctx.beginPath();
ctx.arc(centerX, centerY, size, 0, doublePI, false);
ctx.closePath();
ctx.lineWidth = 10;
//ctx.strokeStyle = blackHex;
ctx.stroke();
}
};
$(function() {
var $venues = $('#venues'),
$venueName = $('#name'),
$venueType = $('#types'),
venueTypes = [],
$list = $('<ul/>'),
$types = $('<ul/>'),
$filterToggler = $('#filterToggle'),
arrayUnique = function(a) {
return a.reduce(function(p, c) {
if (p.indexOf(c) < 0) {
p.push(c);
}
return p;
}, []);
};
$.each(venues, function(index, venue) {
$list.append(
$("<li/>")
.append(
$("<input />").attr({
id: 'venue-' + index,
name: venue.name,
value: venue.name,
type: 'checkbox',
checked: true
})
.change(function() {
var cbox = this,
segments = wheel.segments,
i = segments.indexOf(cbox.value);
if (cbox.checked && i === -1) {
segments.push(cbox.value);
} else if (!cbox.checked && i !== -1) {
segments.splice(i, 1);
}
segments.sort();
wheel.update();
})
).append(
$('<label />').attr({
'for': 'venue-' + index
})
.text(venue.name)
)
);
venueTypes.push(venue.type);
});
$.each(arrayUnique(venueTypes), function(index, venue) {
$types.append(
$("<li/>")
.append(
$("<input />").attr({
id: 'venue-type-' + index,
name: venue,
value: venue,
type: 'checkbox',
checked: true
})
.change(function() {
var $this = $(this),
i;
for (i = 0; i < venues.length; i++) {
if (venues[i].type === $this.val()) {
$('[name="' + venues[i].name + '"]').prop("checked", $this.prop('checked')).trigger('change');
}
}
})
).append(
$('<label />').attr({
'for': 'venue-' + index
})
.text(venue)
)
)
});
$venueName.append($list);
$venueType.append($types);
// Uses the tinysort plugin, but our array is sorted for now.
//$list.find('>li').tsort("input", {attr: "value"});
wheel.init();
$.each($venueName.find('ul input:checked'), function(key, cbox) {
wheel.segments.push(cbox.value);
});
wheel.update();
$venues.slideUp().data("open", false);
$filterToggler.on("click", function() {
if ($venues.data("open")) {
$venues.slideUp().data("open", false);
} else {
$venues.slideDown().data("open", true);
}
});
$('.checkAll').on("click", function() {
$(this).parent().next('div').find('input').prop('checked', $(this).prop('checked')).trigger("change");
});
});
}(jQuery));
body {
background: #fff;
font-family: sans-serif;
color: #666;
}
h1,
h2 {
font-weight: 700;
line-height: 2;
}
input[type='checkbox']+label {
padding-left: .5em;
}
label,
#filterToggle {
display: inline-block;
}
#venues,
#filterToggle {
background: #fefefe;
box-shadow: 0 1px 2px #efefef;
}
#venues {
padding: .5em;
overflow: hidden;
}
#venues ul,
#venues h2 {
clear: both;
}
#venues li {
float: left;
width: 25%;
line-height: 1.5;
vertical-align: middle;
}
#filterToggle {
border-radius: 0 0 3px 3px;
cursor: pointer;
margin-left: .5em;
margin-top: -1px;
padding: .5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="venues">
<h1>Venues <input type="checkbox" class="checkAll" checked /></h1>
<div id="name"></div>
<h2>Types <input type="checkbox" class="checkAll" checked /></h2>
<div id="types"></div>
</div>
<div id="filterToggle">. . .</div>
<div id="wheel">
<canvas id="canvas" width="1000" height="600"></canvas>
</div>
<div id="counter"></div>
How can I change text color and font of the text?
I can help you with the font size and the color:
// Now draw the winning name
ctx.textAlign = "left";
ctx.textBaseline = "middle";
ctx.fillStyle = '#fff000'; // text color
ctx.font = "5em Arial"; // font size and family
You have to edit this part.
(function($) {
var venues = [{
"name": "1900 Mexican Grill",
"type": "Mexican"
}, {
"name": "300 East",
"type": "American/Upscale"
}, {
"name": "Angry Ale's",
"type": "American/Pub "
}, {
"name": "Azteca",
"type": "Mexican"
}, {
"name": "Bedder Bedder and Moore",
"type": "Sandwiches, Salads, Soups"
}, {
"name": "Boading",
"type": "Chinese"
}, {
"name": "Brazwell's Premium Pub",
"type": "American/Pub "
}, {
"name": "Brio Tuscan Grille",
"type": "Italian"
}, {
"name": "Brixx",
"type": "Pizza/Salads"
}, {
"name": "Café at 6100",
"type": "Home Cooking"
}, {
"name": "California Pizza Kitchen",
"type": "Pizza/Salads"
}, {
"name": "Chick-Fil-A",
"type": "Fast Food"
}, {
"name": "City Tavern",
"type": "American/Upscale"
}, {
"name": "Copper",
"type": "Indian"
}, {
"name": "Cowfish",
"type": "Sushi"
}, {
"name": "Duckworth's",
"type": "American/Pub "
}, {
"name": "Eddie's Place",
"type": "American/Pub "
}, {
"name": "El Camino",
"type": "Mexican"
}, {
"name": "Fairview Plaza Restaurant",
"type": "Home Cooking"
}, {
"name": "Firebird's Wood Fired Grill",
"type": "American/Upscale"
}, {
"name": "Firehouse Subs",
"type": "Sandwiches, Salads, Soups"
}, {
"name": "Flying Biscuit",
"type": "American/Fast"
}, {
"name": "Fuel",
"type": "Pizza"
}, {
"name": "Good Food on Montford",
"type": "American/Upscale"
}, {
"name": "Harper's Restaurant",
"type": "American/Upscale"
}, {
"name": "Hawthorne's Pizza",
"type": "Pizza/Italian"
}, {
"name": "Luisa's Brick Oven Pizza",
"type": "Pizza/Italian"
}, {
"name": "Maverick Rock Taco",
"type": "Mexican"
}, {
"name": "McAllister's",
"type": "Sandwiches, Salads, Soups"
}, {
"name": "Mellow Mushroom",
"type": "Pizza/Salads"
}, {
"name": "Moe's",
"type": "Mexican"
}, {
"name": "Moosehead Grill",
"type": "American/Pub "
}, {
"name": "Paco's Tacos and Tequila",
"type": "Mexican"
}, {
"name": "Panera ",
"type": "Sandwiches, Salads, Soups"
}, {
"name": "PF Chang's",
"type": "Chinese"
}, {
"name": "Portofinos",
"type": "Pizza/Italian"
}, {
"name": "Qdoba",
"type": "Mexican"
}, {
"name": "Rooster's Wood Fire Kitchen",
"type": "American/Upscale"
}, {
"name": "Rusty's Deli",
"type": "Sandwiches, Salads, Soups"
}, {
"name": "Taco Bell",
"type": "Fast Food"
}, {
"name": "Taco Mac",
"type": "American/Pub "
}, {
"name": "Terrace Café",
"type": "American/Upscale"
}, {
"name": "The Roasting Company",
"type": "American/Fast"
}, {
"name": "Village Tavern",
"type": "American/Upscale"
}, {
"name": "Which Witch?",
"type": "Sandwiches, Salads, Soups"
}, {
"name": "Zack's Hamburgers",
"type": "American/Fast"
}];
// Helpers
var blackHex = '#333',
whiteHex = '#fff',
shuffle = function(o) {
for (var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x)
;
return o;
},
halfPI = Math.PI / 2,
doublePI = Math.PI * 2;
String.prototype.hashCode = function() {
// See http://www.cse.yorku.ca/~oz/hash.html
var hash = 5381,
i;
for (i = 0; i < this.length; i++) {
char = this.charCodeAt(i);
hash = ((hash << 5) + hash) + char;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
};
Number.prototype.mod = function(n) {
return ((this % n) + n) % n;
};
// WHEEL!
var wheel = {
timerHandle: 0,
timerDelay: 33,
angleCurrent: 0,
angleDelta: 0,
size: 290,
canvasContext: null,
colors: ['#003366', '#FF6600', '#CCCC00', '#006600', '#3333CC', '#CC0066', '#FF3300', '#009900', '#6600CC', '#33CC33', '#0066CC', '#FF0066', '#3300FF', '#00CC00', '#FFCC00'],
segments: [],
seg_colors: [], // Cache of segments to colors
maxSpeed: Math.PI / 16,
upTime: 1000, // How long to spin up for (in ms)
downTime: 5000, // How long to slow down for (in ms)
spinStart: 0,
frames: 0,
centerX: 300,
centerY: 300,
spin: function() {
// Start the wheel only if it's not already spinning
if (wheel.timerHandle == 0) {
wheel.spinStart = new Date().getTime();
wheel.maxSpeed = Math.PI / (16 + Math.random()); // Randomly vary how hard the spin is
wheel.frames = 0;
wheel.sound.play();
wheel.timerHandle = setInterval(wheel.onTimerTick, wheel.timerDelay);
}
},
onTimerTick: function() {
var duration = (new Date().getTime() - wheel.spinStart),
progress = 0,
finished = false;
wheel.frames++;
wheel.draw();
if (duration < wheel.upTime) {
progress = duration / wheel.upTime;
wheel.angleDelta = wheel.maxSpeed *
Math.sin(progress * halfPI);
} else {
progress = duration / wheel.downTime;
wheel.angleDelta = wheel.maxSpeed *
Math.sin(progress * halfPI + halfPI);
if (progress >= 1) {
finished = true;
}
}
wheel.angleCurrent += wheel.angleDelta;
while (wheel.angleCurrent >= doublePI) {
// Keep the angle in a reasonable range
wheel.angleCurrent -= doublePI;
}
if (finished) {
clearInterval(wheel.timerHandle);
wheel.timerHandle = 0;
wheel.angleDelta = 0;
if (console) {
console.log((wheel.frames / duration * 1000) + " FPS");
}
}
/*
// Display RPM
var rpm = (wheel.angleDelta * (1000 / wheel.timerDelay) * 60) / (Math.PI * 2);
$("#counter").html( Math.round(rpm) + " RPM" );
*/
},
init: function(optionList) {
try {
wheel.initWheel();
wheel.initAudio();
wheel.initCanvas();
wheel.draw();
$.extend(wheel, optionList);
} catch (exceptionData) {
alert('Wheel is not loaded ' + exceptionData);
}
},
initAudio: function() {
var sound = document.createElement('audio');
sound.setAttribute('src', 'wheel.mp3');
wheel.sound = sound;
},
initCanvas: function() {
var canvas = $('#canvas')[0];
canvas.addEventListener("click", wheel.spin, false);
wheel.canvasContext = canvas.getContext("2d");
},
initWheel: function() {
shuffle(wheel.colors);
},
// Called when segments have changed
update: function() {
// Ensure we start mid way on a item
//var r = Math.floor(Math.random() * wheel.segments.length);
var r = 0,
segments = wheel.segments,
len = segments.length,
colors = wheel.colors,
colorLen = colors.length,
seg_color = [], // Generate a color cache (so we have consistant coloring)
i
wheel.angleCurrent = ((r + 0.5) / wheel.segments.length) * doublePI;
for (i = 0; i < len; i++) {
seg_color.push(colors[segments[i].hashCode().mod(colorLen)]);
}
wheel.seg_color = seg_color;
wheel.draw();
},
draw: function() {
wheel.clear();
wheel.drawWheel();
wheel.drawNeedle();
},
clear: function() {
wheel.canvasContext.clearRect(0, 0, 1000, 800);
},
drawNeedle: function() {
var ctx = wheel.canvasContext,
centerX = wheel.centerX,
centerY = wheel.centerY,
size = wheel.size,
i,
centerSize = centerX + size,
len = wheel.segments.length,
winner;
ctx.lineWidth = 2;
ctx.strokeStyle = blackHex;
ctx.fillStyle = whiteHex;
ctx.beginPath();
ctx.moveTo(centerSize - 10, centerY);
ctx.lineTo(centerSize + 10, centerY - 10);
ctx.lineTo(centerSize + 10, centerY + 10);
ctx.closePath();
ctx.stroke();
ctx.fill();
// Which segment is being pointed to?
i = len - Math.floor((wheel.angleCurrent / doublePI) * len) - 1;
// Now draw the winning name
ctx.textAlign = "left";
ctx.textBaseline = "middle";
ctx.fillStyle = '#fff000'
ctx.font = "5em Arial";
winner = wheel.segments[i] || 'Choose at least 1 Venue';
ctx.fillText(winner, centerSize + 20, centerY);
},
drawSegment: function(key, lastAngle, angle) {
var ctx = wheel.canvasContext,
centerX = wheel.centerX,
centerY = wheel.centerY,
size = wheel.size,
colors = wheel.seg_color,
value = wheel.segments[key];
//ctx.save();
ctx.beginPath();
// Start in the centre
ctx.moveTo(centerX, centerY);
ctx.arc(centerX, centerY, size, lastAngle, angle, false); // Draw an arc around the edge
ctx.lineTo(centerX, centerY); // Now draw a line back to the center
// Clip anything that follows to this area
//ctx.clip(); // It would be best to clip, but we can double performance without it
ctx.closePath();
ctx.fillStyle = colors[key];
ctx.fill();
ctx.stroke();
// Now draw the text
ctx.save(); // The save ensures this works on Android devices
ctx.translate(centerX, centerY);
ctx.rotate((lastAngle + angle) / 2);
ctx.fillStyle = whiteHex;
ctx.fillText(value.substr(0, 20), size - 15, 0);
ctx.restore();
},
drawWheel: function() {
var ctx = wheel.canvasContext,
angleCurrent = wheel.angleCurrent,
lastAngle = angleCurrent,
len = wheel.segments.length,
centerX = wheel.centerX,
centerY = wheel.centerY,
size = wheel.size,
angle,
i;
ctx.lineWidth = 1;
ctx.strokeStyle = blackHex;
ctx.textBaseline = "middle";
ctx.textAlign = "right";
ctx.font = "1em Arial";
for (i = 1; i <= len; i++) {
angle = doublePI * (i / len) + angleCurrent;
wheel.drawSegment(i - 1, lastAngle, angle);
lastAngle = angle;
}
// Draw a center circle
ctx.beginPath();
ctx.arc(centerX, centerY, 20, 0, doublePI, false);
ctx.closePath();
ctx.fillStyle = whiteHex;
//ctx.strokeStyle = blackHex;
ctx.fill();
ctx.stroke();
// Draw outer circle
ctx.beginPath();
ctx.arc(centerX, centerY, size, 0, doublePI, false);
ctx.closePath();
ctx.lineWidth = 10;
//ctx.strokeStyle = blackHex;
ctx.stroke();
}
};
$(function() {
var $venues = $('#venues'),
$venueName = $('#name'),
$venueType = $('#types'),
venueTypes = [],
$list = $('<ul/>'),
$types = $('<ul/>'),
$filterToggler = $('#filterToggle'),
arrayUnique = function(a) {
return a.reduce(function(p, c) {
if (p.indexOf(c) < 0) {
p.push(c);
}
return p;
}, []);
};
$.each(venues, function(index, venue) {
$list.append(
$("<li/>")
.append(
$("<input />").attr({
id: 'venue-' + index,
name: venue.name,
value: venue.name,
type: 'checkbox',
checked: true
})
.change(function() {
var cbox = this,
segments = wheel.segments,
i = segments.indexOf(cbox.value);
if (cbox.checked && i === -1) {
segments.push(cbox.value);
} else if (!cbox.checked && i !== -1) {
segments.splice(i, 1);
}
segments.sort();
wheel.update();
})
).append(
$('<label />').attr({
'for': 'venue-' + index
})
.text(venue.name)
)
);
venueTypes.push(venue.type);
});
$.each(arrayUnique(venueTypes), function(index, venue) {
$types.append(
$("<li/>")
.append(
$("<input />").attr({
id: 'venue-type-' + index,
name: venue,
value: venue,
type: 'checkbox',
checked: true
})
.change(function() {
var $this = $(this),
i;
for (i = 0; i < venues.length; i++) {
if (venues[i].type === $this.val()) {
$('[name="' + venues[i].name + '"]').prop("checked", $this.prop('checked')).trigger('change');
}
}
})
).append(
$('<label />').attr({
'for': 'venue-' + index
})
.text(venue)
)
)
});
$venueName.append($list);
$venueType.append($types);
// Uses the tinysort plugin, but our array is sorted for now.
//$list.find('>li').tsort("input", {attr: "value"});
wheel.init();
$.each($venueName.find('ul input:checked'), function(key, cbox) {
wheel.segments.push(cbox.value);
});
wheel.update();
$venues.slideUp().data("open", false);
$filterToggler.on("click", function() {
if ($venues.data("open")) {
$venues.slideUp().data("open", false);
} else {
$venues.slideDown().data("open", true);
}
});
$('.checkAll').on("click", function() {
$(this).parent().next('div').find('input').prop('checked', $(this).prop('checked')).trigger("change");
});
});
}(jQuery));
body {
background: #fff;
font-family: sans-serif;
color: #666;
}
h1,
h2 {
font-weight: 700;
line-height: 2;
}
input[type='checkbox']+label {
padding-left: .5em;
}
label,
#filterToggle {
display: inline-block;
}
#venues,
#filterToggle {
background: #fefefe;
box-shadow: 0 1px 2px #efefef;
}
#venues {
padding: .5em;
overflow: hidden;
}
#venues ul,
#venues h2 {
clear: both;
}
#venues li {
float: left;
width: 25%;
line-height: 1.5;
vertical-align: middle;
}
#filterToggle {
border-radius: 0 0 3px 3px;
cursor: pointer;
margin-left: .5em;
margin-top: -1px;
padding: .5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="venues">
<h1>Venues <input type="checkbox" class="checkAll" checked /></h1>
<div id="name"></div>
<h2>Types <input type="checkbox" class="checkAll" checked /></h2>
<div id="types"></div>
</div>
<div id="filterToggle">. . .</div>
<div id="wheel">
<canvas id="canvas" width="1000" height="600"></canvas>
</div>
<div id="counter"></div>

create second thread (web-worker) to run complex function of force layout graph outside of main thread in react

Hi everyone I created a network graph using Pixi and d3 and my graph is to slow for that I have to work complex functions in the second thread
here is App.js
import './App.css';
import { ForceGraph } from './components/forceGraph';
import data from './data/data.json';
import React from 'react';
function App() {
const nodeHoverTooltip = React.useCallback((node) => {
return `<div>
<b>${node.name}</b>
</div>`;
}, []);
return (
<div className="App">
<ForceGraph
linksData={data.links}
nodesData={data.nodes}
nodeHoverTooltip={nodeHoverTooltip}
/>
</div>
);
}
export default App;
here is forceGraph.js
import React from 'react';
import { runForceGraphPixi } from './forceGraphGeneratorPixi';
import styles from './forceGraph.module.css';
export function ForceGraph({ linksData, nodesData, nodeHoverTooltip }) {
const containerRef = React.useRef(null);
React.useEffect(() => {
if (containerRef.current) {
runForceGraphPixi(
containerRef.current,
linksData,
nodesData,
nodeHoverTooltip
);
}
}, [linksData, nodesData, nodeHoverTooltip]);
return <div ref={containerRef} className={styles.container} />;
}
here is forceGraphGeneratorPixi.js
import * as d3 from 'd3';
import * as PIXI from 'pixi.js';
import { Viewport } from 'pixi-viewport';
import styles from './forceGraph.module.css';
export function runForceGraphPixi(
container,
linksData,
nodesData,
nodeHoverTooltip
) {
const links = linksData.map((d) => Object.assign({}, d));
const nodes = nodesData.map((d) => Object.assign({}, d));
const containerRect = container.getBoundingClientRect();
const height = containerRect.height;
const width = containerRect.width;
let dragged = false;
container.innerHTML = '';
const color = () => {
return '#f0f8ff';
};
// Add the tooltip element to the graph
const tooltip = document.querySelector('#graph-tooltip');
if (!tooltip) {
const tooltipDiv = document.createElement('div');
tooltipDiv.classList.add(styles.tooltip);
tooltipDiv.style.opacity = '0';
tooltipDiv.id = 'graph-tooltip';
document.body.appendChild(tooltipDiv);
}
const div = d3.select('#graph-tooltip');
const addTooltip = (hoverTooltip, d, x, y) => {
div.transition().duration(200).style('opacity', 0.9);
div
.html(hoverTooltip(d))
.style('left', `${x}px`)
.style('top', `${y - 28}px`);
};
const removeTooltip = () => {
div.transition().duration(200).style('opacity', 0);
};
const colorScale = (num) => parseInt(color().slice(1), 16);
const app = new PIXI.Application({
width,
height,
antialias: !0,
transparent: !0,
resolution: 1,
});
container.appendChild(app.view);
// create viewport
const viewport = new Viewport({
screenWidth: width,
screenHeight: height,
worldWidth: width * 4,
worldHeight: height * 4,
passiveWheel: false,
interaction: app.renderer.plugins.interaction, // the interaction module is important for wheel to work properly when renderer.view is placed or scaled
});
app.stage.addChild(viewport);
// activate plugins
viewport
.drag()
.pinch()
.wheel()
.decelerate()
.clampZoom({ minWidth: width / 4, minHeight: height / 4 });
const simulation = d3
.forceSimulation(nodes)
.force(
'link',
d3
.forceLink(links) // This force provides links between nodes
.id((d) => d.id) // This sets the node id accessor to the specified function. If not specified, will default to the index of a node.
.distance(50)
)
.force('charge', d3.forceManyBody().strength(-500)) // This adds repulsion (if it's negative) between nodes.
.force('center', d3.forceCenter(width / 2, height / 2))
.force(
'collision',
d3
.forceCollide()
.radius((d) => d.radius)
.iterations(2)
)
.velocityDecay(0.8);
/*
Implementation
*/
let visualLinks = new PIXI.Graphics();
viewport.addChild(visualLinks);
nodes.forEach((node) => {
const { name, gender } = node;
node.gfx = new PIXI.Graphics();
node.gfx.lineStyle(1, 0xd3d3d3);
node.gfx.beginFill(colorScale(node.id));
node.gfx.drawCircle(0, 0, 24);
node.gfx.endFill();
node.gfx
// events for click
.on('click', (e) => {
if (!dragged) {
e.stopPropagation();
}
dragged = false;
});
viewport.addChild(node.gfx);
node.gfx.interactive = true;
node.gfx.buttonMode = true;
// create hit area, needed for interactivity
node.gfx.hitArea = new PIXI.Circle(0, 0, 24);
// show tooltip when mouse is over node
node.gfx.on('mouseover', (mouseData) => {
addTooltip(
nodeHoverTooltip,
{ name },
mouseData.data.originalEvent.pageX,
mouseData.data.originalEvent.pageY
);
});
// make circle half-transparent when mouse leaves
node.gfx.on('mouseout', () => {
removeTooltip();
});
const text = new PIXI.Text(name, {
fontSize: 12,
fill: '#000',
});
text.anchor.set(0.5);
text.resolution = 2;
node.gfx.addChild(text);
});
const ticked = () => {
nodes.forEach((node) => {
let { x, y, gfx } = node;
gfx.position = new PIXI.Point(x, y);
});
for (let i = visualLinks.children.length - 1; i >= 0; i--) {
visualLinks.children[i].destroy();
}
visualLinks.clear();
visualLinks.removeChildren();
visualLinks.alpha = 1;
links.forEach((link) => {
let { source, target, number } = link;
visualLinks.lineStyle(2, 0xd3d3d3);
visualLinks.moveTo(source.x, source.y);
visualLinks.lineTo(target.x, target.y);
});
visualLinks.endFill();
};
// Listen for tick events to render the nodes as they update in your Canvas or SVG.
simulation.on('tick', ticked);
return {
destroy: () => {
nodes.forEach((node) => {
node.gfx.clear();
});
visualLinks.clear();
},
};
}
here is forceGraph.module.css
.container {
width: 100%;
height: 80vh;
position: relative;
background-color: white;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.6);
}
.container svg {
display: block;
width: 100%;
height: 100%;
}
.male {
fill: rgb(22, 130, 218);
}
.female {
fill: rgb(246, 0, 0);
}
.node text {
font: 12px sans-serif;
}
div.tooltip {
position: absolute;
text-align: center;
width: 110px;
padding: 10px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0;
border-radius: 8px;
pointer-events: none;
}
.contextMenu {
stroke: #00557d;
fill: #ffffff;
}
.menuEntry {
cursor: pointer;
}
.menuEntry text {
font-size: 12px;
stroke: #00557d;
}
here is my data.json
{
"nodes": [
{
"id": 1,
"name": "Andy",
"gender": "male"
},
{
"id": 2,
"name": "Betty",
"gender": "female"
},
{
"id": 3,
"name": "Cate",
"gender": "female"
},
{
"id": 4,
"name": "Dave",
"gender": "male"
},
{
"id": 5,
"name": "Ellen",
"gender": "female"
},
{
"id": 6,
"name": "Fiona",
"gender": "female"
},
{
"id": 7,
"name": "Garry",
"gender": "male"
},
{
"id": 8,
"name": "Holly",
"gender": "female"
},
{
"id": 9,
"name": "Iris",
"gender": "female"
},
{
"id": 10,
"name": "Jane",
"gender": "female"
}
],
"links": [
{
"source": 1,
"target": 2
},
{
"source": 1,
"target": 5
},
{
"source": 1,
"target": 6
},
{
"source": 2,
"target": 3
},
{
"source": 2,
"target": 7
}
,
{
"source": 3,
"target": 4
},
{
"source": 8,
"target": 3
}
,
{
"source": 4,
"target": 5
}
,
{
"source": 4,
"target": 9
},
{
"source": 5,
"target": 10
}
]
}
After this I'm getting result
After this i add worker.js,and while I'm importing any other library inside worker.js I'm getting some error but I fixed that one also and the code is below
deep-thought.js(worker)
import addition from './addition';
import * as d3 from 'd3';
/* eslint-disable no-restricted-globals */
self.onmessage = (question) => {
const reciveData = question.data;
const nodes = question.data.nodes;
const links = question.data.links;
console.log(nodes);
console.log(links);
console.log(reciveData);
const result = addition(42);
self.postMessage({
result: result,
});
};
updated forceGraphGeneratorPixi.js
import * as d3 from 'd3';
import * as PIXI from 'pixi.js';
import { Viewport } from 'pixi-viewport';
import styles from './forceGraph.module.css';
export function runForceGraphPixi(
container,
linksData,
nodesData,
nodeHoverTooltip
) {
const links = linksData.map((d) => Object.assign({}, d));
const nodes = nodesData.map((d) => Object.assign({}, d));
const containerRect = container.getBoundingClientRect();
const height = containerRect.height;
const width = containerRect.width;
let dragged = false;
container.innerHTML = '';
const color = () => {
return '#f0f8ff';
};
// Pass data to web-worker
const worker = new Worker(new URL('../deep-thought.js', import.meta.url));
worker.postMessage({
nodes: nodes,
links: links,
});
worker.onmessage = ({ data: { answer } }) => {
console.log(answer);
};
// Add the tooltip element to the graph
const tooltip = document.querySelector('#graph-tooltip');
if (!tooltip) {
const tooltipDiv = document.createElement('div');
tooltipDiv.classList.add(styles.tooltip);
tooltipDiv.style.opacity = '0';
tooltipDiv.id = 'graph-tooltip';
document.body.appendChild(tooltipDiv);
}
const div = d3.select('#graph-tooltip');
const addTooltip = (hoverTooltip, d, x, y) => {
div.transition().duration(200).style('opacity', 0.9);
div
.html(hoverTooltip(d))
.style('left', `${x}px`)
.style('top', `${y - 28}px`);
};
const removeTooltip = () => {
div.transition().duration(200).style('opacity', 0);
};
const colorScale = (num) => parseInt(color().slice(1), 16);
const app = new PIXI.Application({
width,
height,
antialias: !0,
transparent: !0,
resolution: 1,
});
container.appendChild(app.view);
// create viewport
const viewport = new Viewport({
screenWidth: width,
screenHeight: height,
worldWidth: width * 4,
worldHeight: height * 4,
passiveWheel: false,
interaction: app.renderer.plugins.interaction, // the interaction module is important for wheel to work properly when renderer.view is placed or scaled
});
app.stage.addChild(viewport);
// activate plugins
viewport
.drag()
.pinch()
.wheel()
.decelerate()
.clampZoom({ minWidth: width / 4, minHeight: height / 4 });
const simulation = d3
.forceSimulation(nodes)
.force(
'link',
d3
.forceLink(links) // This force provides links between nodes
.id((d) => d.id) // This sets the node id accessor to the specified function. If not specified, will default to the index of a node.
.distance(50)
)
.force('charge', d3.forceManyBody().strength(-500)) // This adds repulsion (if it's negative) between nodes.
.force('center', d3.forceCenter(width / 2, height / 2))
.force(
'collision',
d3
.forceCollide()
.radius((d) => d.radius)
.iterations(2)
)
.velocityDecay(0.8);
/*
Implementation
*/
let visualLinks = new PIXI.Graphics();
viewport.addChild(visualLinks);
nodes.forEach((node) => {
const { name, gender } = node;
node.gfx = new PIXI.Graphics();
node.gfx.lineStyle(1, 0xd3d3d3);
node.gfx.beginFill(colorScale(node.id));
node.gfx.drawCircle(0, 0, 24);
node.gfx.endFill();
node.gfx
// events for click
.on('click', (e) => {
if (!dragged) {
e.stopPropagation();
}
dragged = false;
});
viewport.addChild(node.gfx);
node.gfx.interactive = true;
node.gfx.buttonMode = true;
// create hit area, needed for interactivity
node.gfx.hitArea = new PIXI.Circle(0, 0, 24);
// show tooltip when mouse is over node
node.gfx.on('mouseover', (mouseData) => {
addTooltip(
nodeHoverTooltip,
{ name },
mouseData.data.originalEvent.pageX,
mouseData.data.originalEvent.pageY
);
});
// make circle half-transparent when mouse leaves
node.gfx.on('mouseout', () => {
removeTooltip();
});
const text = new PIXI.Text(name, {
fontSize: 12,
fill: '#000',
});
text.anchor.set(0.5);
text.resolution = 2;
node.gfx.addChild(text);
});
const ticked = () => {
nodes.forEach((node) => {
let { x, y, gfx } = node;
gfx.position = new PIXI.Point(x, y);
});
for (let i = visualLinks.children.length - 1; i >= 0; i--) {
visualLinks.children[i].destroy();
}
visualLinks.clear();
visualLinks.removeChildren();
visualLinks.alpha = 1;
links.forEach((link) => {
let { source, target, number } = link;
visualLinks.lineStyle(2, 0xd3d3d3);
visualLinks.moveTo(source.x, source.y);
visualLinks.lineTo(target.x, target.y);
});
visualLinks.endFill();
};
// Listen for tick events to render the nodes as they update in your Canvas or SVG.
simulation.on('tick', ticked);
return {
destroy: () => {
nodes.forEach((node) => {
node.gfx.clear();
});
visualLinks.clear();
},
};
}
addition.js(perform simple addition in web-worker)
const addition = (data) => {
const result = data + 1;
return result;
};
export default addition;
so i perform simple addition using web-worker but now i want to perform simulation and tick function in webWorker and after performing i want to use it in my code to replace complex function because i'm new to web-worker i'm not figure-out how to do that

Collider in Phaser 3 randomly doesn't work

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.

Change url on image click

I insert an array of images using js, added same class to all images. Now I want to change url when click on image but I cant differentiate between the images can someone please help? For example when click on image1 it takes to a diff link, click on image2 takes to a different link.
here's the code below:
infoArray = [
{
"image": "001.jpg",
"title": "Jordan",
"text": ["Web Development"],
},
{
"image": "tri.jpg",
"title": "Last Night",
"text": ["Web Development"],
},
{
"image": "001.jpg",
"title": "Chatter",
"text": ["Web Development", "Web Design"],
},
]
function insertDiv(array, container) {
array.forEach(function (e, index) {
let div = document.createElement("div");
div.classList.add("box");
container.appendChild(div);
insertImages(array, div, index)
});
}
function insertImages(array, container, index) {
let img = new Image();
img.src = array[index]["image"];
img.classList.add("image");
container.appendChild(img);
insertTitle(array, container, index);
}
function insertTitle(array, container, index) {
let titleContainer = document.createElement("p");
titleContainer.classList.add("title");
let title = document.createTextNode(array[index]["title"]);
titleContainer.appendChild(title);
container.appendChild(titleContainer);
insertText(array, container, index);
}
function insertText(array, container, index) {
let textContainer = document.createElement("div");
textContainer.classList.add("textContainer");
array[index]["text"].forEach(function (e, i) {
let text = document.createElement("p");
let innerText = document.createTextNode(array[index]["text"][i]);
text.appendChild(innerText);
textContainer.appendChild(text);
})
container.appendChild(textContainer);
}
function FadeIn(img){
var ourScene = new ScrollMagic.Scene({
triggerElement: img,
triggerHook: 0.7,
reverse: true,
duration: "100%",
})
.on('start', function(){
TweenMax.to(img, 1, {opacity: 1, y:10})
})
.on('end', function(){
controllerInfo = controller.info("scrollDirection");
TweenMax.to(img, 1, {opacity: 0});
if ( controllerInfo == 'REVERSE'){
TweenMax.to(img, 1, {opacity: 1});
}
})
.addIndicators()
.addTo(controller);
}
function checkHovering(images){ //looks if hovering on img
images.forEach(function(image, index){
document.addEventListener("mousemove", function(e){
if(e.x>image.getBoundingClientRect().x
&& e.x<document.documentElement.clientWidth - image.getBoundingClientRect().x
&& e.y>image.getBoundingClientRect().y
&& e.y<image.getBoundingClientRect().y + image.getBoundingClientRect().height){
TweenMax.to(image, 1, {css: {scale: 1.02}});
appearTitle(index);
} else {
TweenMax.to(image, 0.5, {css: {scale: 1}});
disappearTitle(index);
}
})
})
}
function appearTitle(index){
currentTitles[index].style.opacity = 1;
currentText[index].style.opacity = 1;
}
function disappearTitle(index){
currentTitles[index].style.opacity = 0;
currentText[index].style.opacity = 0;
}
function clickImg(images){
images.forEach(function(image){
})
}
let container = document.querySelector(".container");
let controller = new ScrollMagic.Controller();
insertDiv(infoArray, container);
let currentTitles = document.querySelectorAll(".title");
let currentImages = document.querySelectorAll(".image");
let currentText = document.querySelectorAll(".textContainer");
currentImages.forEach(function(element){
FadeIn(element);
})
checkHovering(currentImages);

EaselJs performance issue for many sprite classes

I would like to ask whether does easeljs has performance issue for many sprite class objects? It seems that from my codepen demo, its terribly lagging...below's code as follows:
var $window = $(window),
wh = $window.innerHeight(),
ww = $window.innerWidth();
var stage = new createjs.Stage("sceneStage");
var w, h, drone;
var runnerSprite2, runnerContainer, drone2, droneContainer, robot;
var robot__movement_speed = 1;
var building_right, building_left;
var queue = new createjs.LoadQueue(),
$state = $('#state'),
$progress = $('#progress'),
$progressbar = $('#progressbar .bar');
queue.on('complete', onComplete);
queue.on('error', onError);
queue.on('fileload', onFileLoad);
queue.on('fileprogress', onFileProgress);
queue.on('progress', onProgress);
queue.loadManifest([
{
id: '2',
src: 'images/sprite_robot_test_plus_toss.png'
},
{
id: '3',
src: 'images/Drones-Hovering-Loop-12fps.png'
},
{
id: '4',
src: 'images/sprite_robot_plus_toss.png'
},
{
id: '5',
src: 'images/sprite_protestor.png'
}
]);
function onComplete(event) {
console.log('Complete', event);
$state.text( $state.text() + '[All loaded]' );
$progressbar.addClass('complete');
$('#mainContainer').addClass('hide');
$('#drone').removeClass('hidden');
loadScenes();
}
function onError(event) {
console.log('Error', event);
$state.text( $state.text() + '[' + event + ' errored] ');
}
function onFileLoad(event) {
console.log('File loaded', event);
$state.text( $state.text() + '[' + event.item.id + ' loaded] ');
}
function onFileProgress(event) {
console.log('File progress', event);
}
function onProgress(event) {
var progress = Math.round(event.loaded * 100);
console.log('General progress', Math.round(event.loaded) * 100, event);
$progress.text(progress + '%');
$progressbar.css({
'width': progress + '%'
});
}
function loadScenes() {
// grab canvas width and height for later calculations:
stage.canvas.width = window.innerWidth;
stage.canvas.height = window.innerHeight;
w = stage.canvas.width;
h = stage.canvas.height;
//----- Drones --------//
var data = new createjs.SpriteSheet({
"images": ["images/Drones-Hovering-Loop-12fps.png"],
"frames": {"regX": 0, "height": 262, "count": 25, "regY": 0, "width": 250},
"animations": {
"idle": [0, 24],
"stun": [0, 0]
},
framerate: 24
});
drone = new createjs.Sprite(data, "idle");
drone.setBounds(null, null, 250, 262);
drone.y = h - drone.getBounds().height;
drone.x = w - drone.getBounds().width;
building_right = drone;
var drone_left = new createjs.Sprite(data, "stun");
drone_left.setBounds(null, null, 250, 262);
drone_left.regX = 250;
drone_left.y = h - drone_left.getBounds().height;
drone_left.x = drone_left.regX;
building_left = drone_left;
droneContainer = new createjs.Container();
droneContainer.addChild(drone, drone_left);
stage.addChild(droneContainer, runnerContainer);
var robot_walk_left_arry = [],
robot_walk_right_arry = [];
for(var i = 14; i< 50; i++) {
robot_walk_left_arry.push(i);
}
for(var i = 49; i > 13; i--) {
robot_walk_right_arry.push(i);
}
console.log(robot_walk_right_arry);
var robot_data = new createjs.SpriteSheet({
"images": ["images/sprite_robot_test_plus_toss.png"],
"frames": {"regX": 0, "height": 540, "count": 83, "regY": 0, "width": 810},
"animations": {
idle: {
frames: [0,1,2,3,4,5,6,7,8,9,10,11,12,11,10,9,8,7,6,5,4,3,2,1]
},
walk_left: {
frames: robot_walk_left_arry,
speed: 1 * robot__movement_speed
},
walk_right: {
frames: robot_walk_right_arry,
speed: 1 * robot__movement_speed
},
toss_left: [50, 82, "idle", 0.8 * robot__movement_speed]
},
framerate: 24
});
robot = new createjs.Sprite(robot_data, "idle");
robot.setBounds(null, null, 810, 540);
robot.regX = 405;
robot.x = (w - robot.getBounds().width);
robot.y = h - robot.getBounds().height;
robot._body_dimen = 162;
stage.addChild(robot);
var protestor_data = new createjs.SpriteSheet({
"images": ["images/sprite_protestor.png"],
"frames": {"regX": 0, "height": 216, "count": 39, "regY": 0, "width": 384},
"animations": {
idle: {
frames: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,13,12,11,10,9,8,7,6,5,4,3,2,1]
},
recovery: [33, 38, "idle", 1],
nudge: [15,33, "recovery", 1]
},
framerate: 24
});
var protestor = new createjs.Sprite(protestor_data, "idle");
protestor.setBounds(null, null, 384, 216);
protestor.x = 200;
protestor.y = h - protestor.getBounds().height;
stage.addChild(protestor);
drone_left.on("click", function() {
tweenthis(robot, robot.x, "left");
});
drone.on("click", function() {
tweenthis(robot, robot.x, "right");
});
createjs.Ticker.framerate = 30;
createjs.Ticker.timingMode = createjs.Ticker.RAF;
createjs.Ticker.addEventListener("tick", tick);
// createjs.Ticker.on("tick", stage);
}
function handleClick(evt, data) {
console.log('test');
}
function tick(event) {
stage.update(event);
}
Generally I just create 4 sprite classes with 3 different sprite images. But didn't expect it to be running so lagging.
The protestors image seems to be 8k x 8k pixel in size (with only the top part filled apparently)... That's about 192 MB unpacked... That will bring down any engine... Make sure your animation images are more efficiently packed and of a more reasonable size...

Categories