There is a new Physics library http://wellcaffeinated.net/PhysicsJS/
I would like to play with it and use bitmap instead of "canvas path".
There is this function in https://github.com/wellcaffeinated/PhysicsJS/blob/master/examples/physicsjs-full.js
drawCircle: function(x, y, r, styles, ctx){
ctx = ctx || this.ctx;
ctx.beginPath();
this.setStyle( styles, ctx );
ctx.arc(x, y, r, 0, Pi2, false);
ctx.closePath();
ctx.stroke();
ctx.fill();
/*
var i = new Image();
i.src = "http://www.pngfactory.net/_png/_thumb/19626-bubka-R2D2.png";
i.onload = function() {
console.log("load");
ctx.drawImage(i, 0, 0);
};
*/
}
But when I add an img in canvas, img.src and load ; nothing happens…
Here is an "official" jsfiddle
http://jsfiddle.net/wellcaffeinated/KkDb6/
and a wiki https://github.com/wellcaffeinated/PhysicsJS/wiki/Fundamentals
Here is a solution made by the author of PhysicsJS :
http://jsfiddle.net/wellcaffeinated/mpbNF/
/**
* PhysicsJS by Jasper Palfree <wellcaffeinated.net>
* http://wellcaffeinated.net/PhysicsJS
*
* Simple Spin example for PhysicsJS
*/
Physics(function(world){
var viewWidth = 500;
var viewHeight = 300;
var renderer = Physics.renderer('canvas', {
el: 'viewport',
width: viewWidth,
height: viewHeight,
meta: false, // don't display meta data
styles: {
// set colors for the circle bodies
'circle' : {
strokeStyle: 'hsla(60, 37%, 17%, 1)',
lineWidth: 1,
fillStyle: 'hsla(60, 37%, 57%, 0.8)',
angleIndicator: 'hsla(60, 37%, 17%, 0.4)'
}
}
});
// add the renderer
world.add( renderer );
// render on each step
world.subscribe('step', function(){
world.render();
});
// bounds of the window
var viewportBounds = Physics.aabb(0, 0, viewWidth, viewHeight);
// constrain objects to these bounds
world.add(Physics.behavior('edge-collision-detection', {
aabb: viewportBounds,
restitution: 0.99,
cof: 0.99
}));
Physics.body('wheel', 'circle', function( parent ){
return {
// no need for an init
// spin the wheel at desired speed
spin: function( speed ){
// the wheels are spinning...
this.state.angular.vel = speed;
}
};
});
var myWheel = Physics.body('wheel', {
x: 40,
y: 30,
radius: 45
});
// Not a hack
myWheel.view = new Image();
myWheel.view.src = 'http://www.large-icons.com/stock-icons/free-large-twitter/round_button-icon.gif';
world.add( myWheel );
// for example, use jquery to listen for a button click, and spin the wheel on the next step
$('button').on('click', function(){
// wait for the next step before spinning the wheel
world.subscribe('step', function( data ){
myWheel.spin( 0.03 );
// only execute callback once
world.unsubscribe( 'step', data.handler );
});
});
// ensure objects bounce when edge collision is detected
world.add( Physics.behavior('body-impulse-response') );
// add some gravity
world.add( Physics.behavior('constant-acceleration') );
// subscribe to ticker to advance the simulation
Physics.util.ticker.subscribe(function( time, dt ){
world.step( time );
});
// start the ticker
Physics.util.ticker.start();
});
Related
I have this map code for MapBox here: How to get click handler on animated canvas dot on MapBox map?
import { useEffect } from 'react'
import * as mapboxgl from 'mapbox-gl'
import locations from 'seeds/sample-location-list.json'
import styles from './Map.module.css'
export default function Map({ scrollable, id, style, onClick }) {
useEffect(() => {
const map = new mapboxgl.Map({
accessToken: '<token>',
container: id ?? 'map',
center: [16.572149246748594, 19.06111670348622],
zoom: 1.5,
style: style ?? 'mapbox://styles/<co>/<style>',
attributionControl: false,
})
// .addControl(new mapboxgl.AttributionControl({
// compact: true
// }), 'top-left')
var size = 120;
// This implements `StyleImageInterface`
// to draw a pulsing dot icon on the map.
var pulsingDot = {
width: size,
height: size,
data: new Uint8ClampedArray(size * size * 4),
context: null,
// When the layer is added to the map,
// get the rendering context for the map canvas.
onAdd: function () {
var canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
this.context = canvas.getContext('2d');
},
// Call once before every frame where the icon will be used.
render: function () {
var duration = 1000;
var t = (performance.now() % duration) / duration;
var radius = (size / 2) * 0.3;
var outerRadius = (size / 2) * 0.7 * t + radius;
var context = this.context;
// Draw the outer circle.
context.clearRect(0, 0, this.width, this.height);
context.beginPath();
context.arc(
this.width / 2,
this.height / 2,
outerRadius,
0,
Math.PI * 2
);
context.fillStyle = 'rgba(253, 253, 150,' + (1 - t) + ')';
context.fill();
// Draw the inner circle.
context.beginPath();
context.arc(
this.width / 2,
this.height / 2,
radius,
0,
Math.PI * 2
);
context.fillStyle = 'rgba(253, 253, 150, 1)';
context.strokeStyle = 'rgb(119, 221, 119)';
context.lineWidth = 2 + 4 * (1 - t);
context.fill();
context.stroke();
// Update this image's data with data from the canvas.
this.data = context.getImageData(
0,
0,
this.width,
this.height
).data;
// Continuously repaint the map, resulting
// in the smooth animation of the dot.
map.triggerRepaint();
// Return `true` to let the map know that the image was updated.
return true;
}
};
if (!scrollable) map.scrollZoom.disable()
map.on('load', function () {
map.addImage('pulsing-dot', pulsingDot, { pixelRatio: 2 });
locations.forEach(location => {
map.addSource(location.id.seed, {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [location.lng, location.lat] // icon position [lng, lat]
},
properties: {},
}
]
}
})
map.addLayer({
'id': location.id.seed,
'type': 'symbol',
'source': location.id.seed,
'layout': {
'icon-image': 'pulsing-dot'
}
});
})
map.on('mousemove', 'earthquakes-viz', (e) => {
// console.log
})
});
if (onClick) map.on('click', onClick)
return () => map.remove()
}, [])
return (
<div id={id ?? "map"} className={styles.map}></div>
)
}
It animates the pulsing of around 20 dots on a map canvas layer. I checked it after some time and it was at 80MB memory usage (my whole React app, of which the map is a small part part), yet my CPU was screaming, fans going off like crazy. How do I fix it so it's not so processor intensive?
Chrome says:
Canvas2D: Multiple readback operations using getImageData are faster with the willReadFrequently attribute set to true. See: https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-will-read-frequently
on .getImageData
// update this image's data with data from the canvas
this.data = context.getImageData(
The code is still in the Mapbox example but on first sight looks outdated and not very performance optimised. Did you found a solution to this?
Heres the issue:
https://github.com/mapbox/mapbox-gl-js/pull/12397
I know in traditional HTML5 canvas, we can use drawImage method (the longest one with 9 properties) and change frameX and frameY to make sprite sheet animation. But I am new to matter.js. I've checked matter.js document but still don't have any idea about how to animate my sprite. Here is my object:
const ball = Bodies.circle(340, 340, 10, {
density: 0.0005,
frictionAir: 0.06,
restitution: 0,
friction: 0,
render: {
sprite: {
texture: "images/blueMonster.png",
yScale: 0.2,
xScale: 0.2,
isStatic: true,
},
},
inertia: Infinity,
label: "ball",
});
World.add(world, ball);
If I need to provide more info to solve this problem, please let me know. Thank you very much for your time!
There may be a fundamental misconception here. Matter.js is a physics engine that can plug into any rendering front-end. You don't have to use the built-in MJS rendering engine which is primarily there for prototyping. You can use your existing HTML5 code or something like Phaser which has robust support for sprite sheets.
Here's a simple proof-of-concept using vanilla JS to render a sprite animation with MJS as the physics engine. The approach is to call Matter.Engine.update(engine); to run the engine each frame and use coin.position to draw the sprite. More complex animations might use vertices and angle as shown here and here in addition to the sprite sheet, but this is use-case dependent.
(async () => {
const image = await new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => resolve(image);
image.onerror = reject;
image.src = "https://art.pixilart.com/c7f297523ce57fc.png";
});
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
canvas.width = 500;
canvas.height = 250;
const engine = Matter.Engine.create();
const coin = Matter.Bodies.circle(100, 0, 100, {
density: 0.0005,
frictionAir: 0.06,
restitution: 0,
friction: 0,
});
const ground = Matter.Bodies.rectangle(
0, 350, 1500, 170, {isStatic: true}
);
const mouseConstraint = Matter.MouseConstraint.create(
engine, {element: canvas}
);
Matter.Composite.add(
engine.world, [coin, ground, mouseConstraint]
);
const w = 200;
const h = 170;
let frameNumber = 0;
(function rerender() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const offset = (~~frameNumber * w) % image.width;
const {x, y} = coin.position;
ctx.drawImage(
image, // image
offset, // sx
40, // sy
w, // sWidth
h, // sHeight
x - w / 2, // dx
y - h / 2, // dy
w, // dWidth
h // dHeight
);
frameNumber += 0.1;
Matter.Engine.update(engine);
requestAnimationFrame(rerender);
})();
})();
canvas {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
<canvas></canvas>
Another approach is to plop in a gif or video--easily done since you're just making a webpage in the DOM that happens to have physics attached. See how to round an image in matter.js for an example.
I can't work out why my sprites are not working in matter.js. I am creating a car/football game and have disabled wireframes on the renderer but the sprites are still not applying correctly.
My first issue is that when I apply a sprite texture to a composite body (the car), the sprite does not render at all.
My second issue is that when I apply a sprite texture to the body of the car, the sprite does not rotate with the body (the sprite does not rotate at all).
My third issue is that when I apply a sprite texture to the ball (not a composite body), both the sprite and the body representing the ball become invisible. They ball is still visible to the engine but neither the body or the sprite can be seen on the canvas.
function game ()
{
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// module aliases
var Engine = Matter.Engine,
Render = Matter.Render,
World = Matter.World,
Bodies = Matter.Bodies,
Body = Matter.Body,
Composite = Matter.Composite,
Composites = Matter.Composites,
Vertices = Matter.Vertices;
// create an engine
var engine = Engine.create();
engine.world.gravity.y = 0.6;
// create a renderer
var render = Render.create
({
//element: canvas,
element: document.body,
canvas: canvas,
engine: engine,
options:
{
width: window.innerWidth - 30,
height: window.innerHeight - 30,
wireframes: false
}
});
var offset = 1;
var wallSize = 20;
var ground = Bodies.rectangle(400, 510, 1810, 60,
{
isStatic: true,
friction: 0,
restitution: 0
});
var ball = Bodies.circle(window.innerWidth/2, window.innerHeight/2, 40,
{
mass: 5,// Used to be 0.5
restitution: 0.95,
friction: 0,
frictionAir: 0.01,
});
ball.render.sprite.texture = "soccarball.png";
const carBody = Matter.Bodies.fromVertices(100, 100, [{x:200, y:200},{x:260, y:210},{x:260, y:220},{x: 200, y: 220}]);
carBody.render.sprite.texture = "car_sprites.jpg";
carBody.render.sprite.xScale = 0.06;
carBody.render.sprite.yScale = 0.06;
const frontWheel = Matter.Bodies.circle(100 -20, 115, 8);
const rearWheel = Matter.Bodies.circle(100 +20, 115, 8);
const car = Body.create
({
parts: [carBody, frontWheel, rearWheel],
inertia: 100000,
friction: 0,
mass: 100,
restitution: -1,
});
var floor = Bodies.rectangle(window.innerWidth/2, window.innerHeight + offset, window.innerWidth + 2 * offset, wallSize,
{
isStatic: true, friction: 0
});
World.add(engine.world, [ground, car, ball, floor]);
// MAIN lOOP
function cycle()
{
requestAnimationFrame(cycle);
}
cycle();
// run the engine
Engine.run(engine);
//Engine.update(engine);
// run the renderer
Render.run(render);
}
window.onload = game();
///////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
function checkButtons()
{
if(keys["68"]) // KEY_D
{
if(car.speed < 10)
{
//Body.applyForce( car, {x: car.position.x, y: car.position.y}, {x: 0.5, y: 0})
let force = (0.001 * car.mass);
Body.applyForce(car,car.position,{x:force,y:0});
//console.log("Car Speed: " + car.speed);
}
}
if(keys["87"]) // KEY_W
{
if(onGround())
{
carAvailableJumps--;
let verticalForce = (-0.013 * car.mass);
Body.applyForce(car,car.position,{x:0,y:verticalForce});
}
if(carAvailableJumps > 0)
{
if(!onGround() && keys["68"]) // KEY_D
{
carAvailableJumps--;
let rotationalForce = (0.0009 * car.mass);
Body.applyForce(car,{x: (car.position.x - carWidth/2), y: (car.position.y)},{x:0,y:-rotationalForce});
}
if(!onGround() && keys["65"]) // KEY_A
{
carAvailableJumps--;
let rotationalForce = (0.0009 * car.mass);
Body.applyForce(car,{x: (car.position.x - carWidth/2), y: (car.position.y)},{x:0,y:rotationalForce});
}
}
}
if(keys["83"]) // KEY_S
{
}
if(keys["65"]) // KEY_A
{
if(car.speed < 10)
{
//Body.applyForce( car, {x: car.position.x, y: car.position.y}, {x: 0.5, y: 0})
let force = (-0.001 * car.mass);
Body.applyForce(car,car.position,{x:force,y:0});
//console.log("Car Speed: " + car.speed);
}
}
}
*/
<!DOCTYPE HTML>
<html>
<meta charset="UTF-8"/>
<head>
<title>This is the title</title>
</head>
<body>
<div id="div">
<canvas id="myCanvas"</canvas>
<script src="matter.js" type="text/javascript"></script>
<script src="internethelp.js" type="text/javascript"></script>
</div>
</body>
</html>
You are supposed to draw the car and other sprites yourself on the canvas. Matter only calculates the coordinates in the physics world for you. You have to apply those coordinates to the objects that you draw in your canvas.
This is an example where the matter coordinates are read, and then used to position a DIV element in the DOM
let physicsBox = Matter.Bodies.rectangle(x, y, w, h);
Matter.World.add(world, [physicsBox]);
let div = document.createElement("box");
document.body.appendChild(div);
// draw
let pos = physicsBox.position;
let angle = physicsBox.angle;
let degrees = angle * (180 / Math.PI);
div.style.transform = "translate(" + (pos.x - 10) + "px, " + (pos.y - 10) + "px) rotate(" + degrees + "deg)";
I am trying to create spikes on earth(sphere geometry). Though everything works fines, but spikes dont align with globe. I want spike to align something like below image. But my spikes dont lookAt(new THREE.Vector3(0,0,0)) despite mentioned. Please help me out.
I purposefully mentioned code required for debugging. Let me know if you need more code for this. Below image is how i want my spikes to align with sphere.
But this is how it looks
My Main JS initialization file.
$(document).ready(function () {
// Initializing Camera
Influx.Camera = new Influx.Camera({
fov: 60,
aspectRatio: window.innerWidth / window.innerHeight,
near: 1,
far: 1000,
position: {
x: 0,
y: 0,
z: 750
}
});
//Initializing Scene
Influx.Scene = new Influx.Scene();
// Initializing renderer
Influx.Renderer = new Influx.Renderer({
clearColor: 0x000000,
size: {
width: window.innerWidth,
height: window.innerHeight
}
});
Influx.Globe = new Influx.Globe({
radius: 300,
width: 50,
height: 50
});
//
Influx.Stars = new Influx.Stars({
particleCount: 15000,
particle: {
color: 0xFFFFFF,
size: 1
}
});
Influx.moveTracker = new Influx.moveTracker();
Influx.EventListener = new Influx.EventListener();
(function animate() {
requestAnimationFrame( animate );
render();
controls.update();
})();
function render() {
camera.lookAt(scene.position);
group.rotation.y -= 0.001;
renderer.render( scene, camera );
};
});
Below is code responsible for generating spikes on Globe.
Influx.Spikes = function (lat, long) {
// convert the positions from a lat, lon to a position on a sphere.
var latLongToVector3 = function(lat, lon, RADIUS, heigth) {
var phi = (lat) * Math.PI/180,
theta = (lon-180) * Math.PI/180;
var x = -(RADIUS+heigth) * Math.cos(phi) * Math.cos(theta),
y = (RADIUS+heigth) * Math.sin(phi),
z = (RADIUS+heigth) * Math.cos(phi) * Math.sin(theta);
return new THREE.Vector3(x, y, z);
};
var geom = new THREE.Geometry();
var BoxGeometry = new THREE.BoxGeometry(1, 100, 1);
//iterates through the data points and makes boxes with the coordinates
var position = latLongToVector3(lat, long, 300, 2);
var box = new THREE.Mesh( BoxGeometry );
//each position axis needs to be set separately, otherwise the box
//will instantiate at (0,0,0)
box.position.x = position.x;
box.position.y = position.y;
box.position.z = position.z;
box.lookAt(new THREE.Vector3(0, 0, 0));
box.updateMatrix();
//merges the geometry to speed up rendering time, don't use THREE.GeometryUtils.merge because it's deprecated
geom.merge(box.geometry, box.matrix);
var total = new THREE.Mesh(geom, new THREE.MeshBasicMaterial({
color: getRandomColor(),
morphTargets: true
}));
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
//add boxes to the group
group.add(total);
scene.add(group);
};
Influx.Camera = function(params = {}) {
if ( !$.isEmptyObject(params) ) {
window.camera = new THREE.PerspectiveCamera(params.fov, params.aspectRatio, params.near, params.far);
camera.position.set(params.position.x, params.position.y, params.position.z);
camera.lookAt(new THREE.Vector3(0,0,0));
} else {
console.log("Trouble with Initializing Camera");
return;
}
};
Remember that lookAt takes a direction vector, you give to this method the vector (0, 0, 0), this is actually not a normalized direction vector. So you must calculate the direction:
from your box position to the center of the sphere AND normalize it.
var dir = box.position.sub(world.position).normalize();
box.lookAt(dir);
And now just a set of code good conventions that may help you:
var BoxGeometry = new THREE.BoxGeometry(1, 100, 1);
Here I would rather use another var name for the box geometry, not to mix up with the "class" definition from THREE and to follow naming conventions:
var boxGeometry = new THREE.BoxGeometry(1, 100, 1);
And here:
box.position.x = position.x;
box.position.y = position.y;
box.position.z = position.z;
You can just set:
box.position.copy(position);
I also meet this problem, and I fixed it, the solution is: box.lookAt(new THREE.Vector3(0, 0, 0)) must after box.scale.z = xxxx
I have t his effect on this website
http://www.immersive-garden.com/
there's this point of light sparking, on hover you get the background, I want something similar without using flash
this is the script I'm using right now
/*
Particle Emitter JavaScript Library
Version 0.3
by Erik Friend
Creates a circular particle emitter of specified radius centered and offset at specified screen location. Particles appear outside of emitter and travel outward at specified velocity while fading until disappearing in specified decay time. Particle size is specified in pixels. Particles reduce in size toward 1px as they decay. A custom image(s) may be used to represent particles. Multiple images will be cycled randomly to create a mix of particle types.
example:
var emitter = new particle_emitter({
image: ['resources/particle.white.gif', 'resources/particle.black.gif'],
center: ['50%', '50%'], offset: [0, 0], radius: 0,
size: 6, velocity: 40, decay: 1000, rate: 10
}).start();
*/
particle_emitter = function (opts) {
// DEFAULT VALUES
var defaults = {
center: ['50%', '50%'], // center of emitter (x / y coordinates)
offset: [0, 0], // offset emitter relative to center
radius: 0, // radius of emitter circle
image: 'particle.gif', // image or array of images to use as particles
size: 1, // particle diameter in pixels
velocity: 10, // particle speed in pixels per second
decay: 500, // evaporation rate in milliseconds
rate: 10 // emission rate in particles per second
};
// PASSED PARAMETER VALUES
var _options = $.extend({}, defaults, opts);
// CONSTRUCTOR
var _timer, _margin, _distance, _interval, _is_chrome = false;
(function () {
// Detect Google Chrome to avoid alpha transparency clipping bug when adjusting opacity
if (navigator.userAgent.indexOf('Chrome') >= 0) _is_chrome = true;
// Convert particle size into emitter surface margin (particles appear outside of emitter)
_margin = _options.size / 2;
// Convert emission velocity into distance traveled
_distance = _options.velocity * (_options.decay / 1000);
// Convert emission rate into callback interval
_interval = 1000 / _options.rate;
})();
// PRIVATE METHODS
var _sparkle = function () {
// Pick a random angle and convert to radians
var rads = (Math.random() * 360) * (Math.PI / 180);
// Starting coordinates
var sx = parseInt((Math.cos(rads) * (_options.radius + _margin)) + _options.offset[0] - _margin);
var sy = parseInt((Math.sin(rads) * (_options.radius + _margin)) + _options.offset[1] - _margin);
// Ending Coordinates
var ex = parseInt((Math.cos(rads) * (_options.radius + _distance + _margin + 0.5)) + _options.offset[0] - 0.5);
var ey = parseInt((Math.sin(rads) * (_options.radius + _distance + _margin + 0.5)) + _options.offset[1] - 0.5);
// Pick from available particle images
var image;
if (typeof(_options.image) == 'object') image = _options.image[Math.floor(Math.random() * _options.image.length)];
else image = _options.image;
// Attach sparkle to page, then animate movement and evaporation
var s = $('<img>')
.attr('src', image)
.css({
zIndex: 10,
position: 'absolute',
width: _options.size + 'px',
height: _options.size + 'px',
left: _options.center[0],
top: _options.center[1],
marginLeft: sx + 'px',
marginTop: sy + 'px'
})
.appendTo('body')
.animate({
width: '1px',
height: '1px',
marginLeft: ex + 'px',
marginTop: ey + 'px',
opacity: _is_chrome ? 1 : 0
}, _options.decay, 'linear', function () { $(this).remove(); });
// Spawn another sparkle
_timer = setTimeout(function () { _sparkle(); }, _interval);
};
// PUBLIC INTERFACE
// This is what gets returned by "new particle_emitter();"
// Everything above this point behaves as private thanks to closure
return {
start:function () {
clearTimeout(_timer);
_timer = setTimeout(function () { _sparkle(); }, 0);
return(this);
},
stop:function () {
clearTimeout(_timer);
return(this);
},
centerTo:function (x, y) {
_options.center[0] = x;
_options.center[1] = y;
},
offsetTo:function (x, y) {
if ((typeof(x) == 'number') && (typeof(y) == 'number')) {
_options.center[0] = x;
_options.center[1] = y;
}
}
}
};
you probably need something like this: http://www.realcombiz.com/2012/09/customize-blackquote-with-light-bulb.html