pts.js: Speed up animation of grid cells? - javascript

I'm using Pts.js to create a grid of cells and then color these cells depending on their distance to the mouse pointer. My code is largely based on a demo from the official Pts.js Website.
Pts.quickStart("#pt", "#123");
//
let pts = [];
var follower = new Pt();
space.add({
start: (bound) => {
pts = Create.gridCells(space.innerBound, 40, 20);
follower = space.center;
},
//
animate: (time, ftime) => {
follower = follower.add(space.pointer.$subtract(follower).divide(20));
form.stroke("#123");
//
let rects = pts.map((p) => {
let color;
let mag = follower.$subtract(Rectangle.center(p)).magnitude();
let r = Rectangle.fromCenter(Rectangle.center(p), Rectangle.size(p));
//
if (mag >= 100) {
color = "#000"
} else {
color = "#f00"
}
//
form.fill(color).rect(r);
})
//
form.fillOnly("#fff").point(space.pointer, 10, "circle");
}
})
//
space.bindMouse().bindTouch().play();
#pt {
width: 800px;
height: 600px;
margin: 30px auto 0;
}
<script src="https://unpkg.com/pts#0.9.4/dist/pts.min.js"></script>
<div id="pt"></div>
The implementation works absolutely fine. But I'd like to increase the speed with which the »coloring« of the cells »follows« the cursor, i.e. reduce the delay with which the red space around the cursor is animated. Ideally, I'd like to have no delay.
I'm new to Pts.js, so still wrapping my head around the docs, and I can't find an option or explanation for how to control the animation's speed. If anyone could point me to what I need to do here, that'd be great.

It seems that this line is what controls the behavior of the red grid area:
follower = follower.add(space.pointer.$subtract(follower).divide(20));
The value supplied to .divide() controls the speed at which the red area follows the cursor. Changing its argument from 20 to 1 (or even removing .divide(20) entirely) causes the "following" behavior to be immediate.
(Though, if you intend to remove the capability for that behavior, I suspect that entire line could be simplified.)

Related

How can I make a collider for only the bottom of objects?

For a 2D top-down game made with Phaser, the player should be able to walk past the front and back of furniture, which I've achieved by changing the depth of the player according to the y value of their position. However, they should not be able to walk through furniture by moving down through it (or moving up through it). They also be blocked from walking through it by walking at the furniture from the sides when at the same y value (coming at the furniture from a higher/lower value is fine, they then just go behind/in front of the furniture).
I figure this could be made by blocking collisions between the bottom of the player sprite and the bottom x pixels of furniture. But I don't know how to do this and can't find anything in the documentation or online. I figure I can make small invisible objects that go in the bottom of the player and furniture, but is there a simpler way to achieve this effect?
I have the furniture grouped by depth, and for each depth have code like this:
create {
furnitureBack = this.physics.add.staticGroup();
furnitureBack.create(300, 80, 'furniture').setFrame(60).refreshBody();
furnitureBack.setDepth(2);
colliderBack = this.physics.add.collider(player, furnitureBack);
}
update {
colliderBack.active = false;
if (player.y > 75 && player.y < 85) {
colliderBack.active = true;
}
}
The problem with this is that it only works when the player enters the 75-85 y range without already overlapping with the furniture. If they enter that range by walking towards the furniture from the bottom, then the collider becomes active but the player is still able to walk through the item.
An easy and fast solution would be to use phaser physics, and just alter the size of the body.
Here the link to the body Documentation (you could even make the collision box small and round with setCircle, here is the link Documentation)
Here a working demo:
(the white box is the player the brown box, is a box)
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 536/2,
height: 183/2,
zoom: 2,
physics: {
default: 'arcade',
arcade: {
debug: true
}
},
scene: {
create,
update
},
banner: false
};
function create () {
this.add.text(10, 10, 'Use cursor-keys to move')
this.player = this.add.rectangle(50, 80, 20, 40, 0xffffff)
.setOrigin(.5, 1)
.setDepth(1);
this.box = this.add.rectangle(100, 80, 20, 20, 0x933B26)
.setOrigin(.5, 1)
.setDepth(2);
this.physics.add.existing(this.box, true)
.body
//Setup physics-body size and position
.setSize(20, 10, false)
.setOffset(0, 10);
this.physics.add.existing(this.player)
.body
//Setup physics-body size and position
.setSize(20, 20, false)
.setOffset(0, 20);
this.keys = this.input.keyboard.createCursorKeys();
this.physics.add.collider(this.player, this.box)
}
function update(){
if(!this.player || !this.player.body){
return;
}
let body = this.player.body;
let speed = 50;
body.setVelocity(0, 0);
// Since the box depth is set to "2"
// you just have to alternate the depth of the player
let newDepth = (this.player.y > this.box.y) ? 3 : 1;
this.player.setDepth(newDepth);
if(this.keys.right.isDown){
body.setVelocity(speed, 0);
}
if(this.keys.left.isDown){
body.setVelocity(-speed, 0);
}
if(this.keys.up.isDown){
body.setVelocity(0, -speed);
}
if(this.keys.down.isDown){
body.setVelocity(0, speed);
}
}
new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>

Overlapping image inside a table

Question: how 2 images can live in the same td ? how can I overlap the first one?
Bug: the images doesn't overlapping, sometimes the character's image is printed next to the tiles images instead to overlap it.
I will link to you the pen of this little program, try to random generate different times to occur in this bug.
penHere
After the first sections of customization a random map will generate the position of the characters.
I investigate over this bug, and i discover that it's not a problem of coordinates ( they are random generated with this function )
function coordinate(){
let rowCoord= map.length;
let cellCoord = map[1].length;
let coord = {
row: Math.floor(Math.random() * rowCoord),
cell: Math.floor(Math.random() * cellCoord)
}
return coord;
};
// this function will place the character if the coordinates are ok. Else we have a callback to the same function.
function placeCharAndItem(char){
let coord = coordinate();
// with this if you choose a walkable table to spawn, this is random generated
if(map[coord.row][coord.cell] === 0 ){
place(coord, char);
}
else{
placeCharAndItem(char);
}
};
The map is random generated too. It's something like this.
map = [[1,1,1,1,0],
[1,0,0,0,0],
[1,0,1,1,1],
[1,0,0,0,1],
[1,1,1,0,1]]
and this is the function that let the character image spawn on the right td
function place(coord, char){
var charImage = $("<img>").attr("src", char.image).addClass('char');
var row = $($("#tableGame tr")[coord.row]);
var cell = $($("td", row)[coord.cell]);
cell.append(charImage);
};
Thanks :)
If you just put two images in a table cell, they will be displayed one after the other by default, this is just how the HTML works.
To make overlap of two images you can explicitly set position of the second image relative to it's parent table cell. You can do it with CSS:
Apply position: relative; to the parent td;
Apply:
position: absolute;
top: 0;
left: 0;
to the second image (and all next images in the same cell, if you will have more).
Keep in mind that second image now will be out of the standard HTML flow, it will no longer affect the cell size, it will overlap anything. You may need to explicitly set the cell size.
Also you can set this styles dynamically with the JQuery:
// Get your table cell and image somehow.
const $cell = $(...);
const $image = $(...);
$cell.css('position', 'relative');
$image.css({
position: 'absolute',
top: 0,
left: 0,
});
Or with a plain JS:
const cell = document.querySelector(...);
const image = document.querySelector(...);
cell.style.position = 'relative';
image.style.position = 'absolute';
image.style.top = 0;
image.style.left = 0;

Cesium: "fan out" overlapping polylines?

I am using Cesium and am looking to visually represent multiple polylines between the same two entities. For example, a green polyline from entity A to entity B, and also a blue polyline from entity A to entity B. I would like them not to overlap or blend, so I am imagining a fanning out as more lines are drawn, so that each line and what it represents can be visualized. I've included a crude drawing of what I'm trying to explain with the fanning out rather than overlapping.
I have a functional data structure keeping track of the lines I want to represent, as well as a Cesium map that they are already being programatically drawn on. I guess at this point I'm looking for the technical explanation of how to programatically bend the polylines on the map, and also any suggestions for polyline management in order to recognize overlapping lines so I can apply the bends.
Thanks for any help!
Here's one method. This sample code will "spread" the lines along longitude only, so works best on North/South lines and not on East/West lines. But I think it should convey the right idea, you just have to figure out a more general-purpose way of "moving" the midpoint to a visually pleasing location.
I'm using time-based paths here, to gain access to Cesium's interpolation logic. But I've selected a reference time far in the past, and I'm only showing the finished paths on the viewer. So, the user is none the wiser that time is playing any role here.
var viewer = new Cesium.Viewer('cesiumContainer', {
navigationInstructionsInitiallyVisible: false,
animation: false,
timeline: false,
// These next 5 lines are just to avoid the Bing Key error message.
imageryProvider : Cesium.createTileMapServiceImageryProvider({
url : Cesium.buildModuleUrl('Assets/Textures/NaturalEarthII')
}),
baseLayerPicker : false,
geocoder : false,
// This next line fixes another Stack Snippet error, you may omit
// this setting from production code as well.
infoBox : false
});
var numberOfArcs = 5;
var startLon = -105;
var startLat = 39.7;
var stopLon = -98.4;
var stopLat = 29.4;
var spread = 5;
var referenceTime = Cesium.JulianDate.fromIso8601('2001-01-01T00:00:00Z');
var midTime = Cesium.JulianDate.addSeconds(referenceTime, 43200, new Cesium.JulianDate());
var stopTime = Cesium.JulianDate.addSeconds(referenceTime, 86400, new Cesium.JulianDate());
for (var i = 0; i < numberOfArcs; ++i) {
var color = Cesium.Color.fromRandom({
alpha : 1.0
});
// Create a straight-line path.
var property = new Cesium.SampledPositionProperty();
var startPosition = Cesium.Cartesian3.fromDegrees(startLon, startLat, 0);
property.addSample(referenceTime, startPosition);
var stopPosition = Cesium.Cartesian3.fromDegrees(stopLon, stopLat, 0);
property.addSample(stopTime, stopPosition);
// Find the midpoint of the straight path, and move it.
var spreadAmount = (spread / (numberOfArcs - 1)) * i - (spread / 2);
var midPoint = Cesium.Cartographic.fromCartesian(property.getValue(midTime));
midPoint.longitude += Cesium.Math.toRadians(spreadAmount);
var midPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(
midPoint, new Cesium.Cartesian3());
// Redo the path to be the new arc.
property = new Cesium.SampledPositionProperty();
property.addSample(referenceTime, startPosition);
property.addSample(midTime, midPosition);
property.addSample(stopTime, stopPosition);
// Create an Entity to show the arc.
var arcEntity = viewer.entities.add({
position : property,
// This path shows the arc as a polyline.
path : {
resolution : 1200,
material : new Cesium.PolylineGlowMaterialProperty({
glowPower : 0.16,
color : color
}),
width : 10,
leadTime: 1e11,
trailTime: 1e11
}
});
// This is where it becomes a smooth path.
arcEntity.position.setInterpolationOptions({
interpolationDegree : 5,
interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
});
}
// Optionally, add start and stop points, mostly for easy zoomTo().
viewer.entities.add({
position : Cesium.Cartesian3.fromDegrees(startLon, startLat),
point : {
pixelSize : 8,
color : Cesium.Color.WHITE
}
});
viewer.entities.add({
position : Cesium.Cartesian3.fromDegrees(stopLon, stopLat),
point : {
pixelSize : 8,
color : Cesium.Color.WHITE
}
});
viewer.zoomTo(viewer.entities);
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
font-family: sans-serif;
}
<link href="http://cesiumjs.org/releases/1.30/Build/Cesium/Widgets/widgets.css"
rel="stylesheet"/>
<script src="http://cesiumjs.org/releases/1.30/Build/Cesium/Cesium.js">
</script>
<div id="cesiumContainer"></div>

amcharts customized label position

Right now my graph's label is placed in a default position, but my label texts are rather long and its sticking out from the graph.
If possible, I want to place it so that the labels don't stick out from the graph.
Pic of Current situation & Ideal graph
Or if the above is not possible, I want to fix the visible labels. Is this possible?
right now, the "best" label gets lost/hidden when the graph width is reduced. The "too much" label also gets lost when the width is largely reduced.
I have been searching all over stackoverflow and amcharts website but I can't seem to find a good solution.
Is there any way to solve either of the problems...?
// tried these but doesnt work for what I want to do
va.labelOffset = -5;
va.position = "bottom";
va.inside = false;
full code in JSfiddle
In order to keep the chart from hiding the labels on resize, you need to disable the valueAxis' autoGridCount and set the gridCount to the number of tick marks you want to see. You'll also need to remove the labelFrequency setting.
va.autoGridCount = false;
va.gridCount = 3;
//va.labelFrequency = 5;
As for label positioning, you are limted to rotating and vertical offsets out of the box. As a workaround, you can position the labels by modifying the SVG nodes directly through the drawn event, for example:
// prior to chart.write
chart.addListener("drawn", function(e) {
var textNodes = e.chart.valueAxes[0].labelsSet.node.childNodes;
var transform;
//Position "too little"
transform = parseTransform(textNodes[0].getAttribute('transform'));
transform.translate[0] = parseFloat(transform.translate[0]) + 25;
textNodes[0].setAttribute('transform', serializeTransform(transform));
// Position "too much"
transform = parseTransform(textNodes[2].getAttribute('transform'));
transform.translate[0] = parseFloat(transform.translate[0]) - 25;
textNodes[2].setAttribute('transform', serializeTransform(transform));
});
// ...
// from http://stackoverflow.com/questions/17824145/parse-svg-transform-attribute-with-javascript
function parseTransform(a) {
var b = {};
for (var i in a = a.match(/(\w+\((\-?\d+\.?\d*e?\-?\d*,?)+\))+/g)) {
var c = a[i].match(/[\w\.\-]+/g);
b[c.shift()] = c;
}
return b;
}
//serialize transform object back to an attribute string
function serializeTransform(transformObj) {
var transformStrings = [];
for (var attr in transformObj) {
transformStrings.push(attr + '(' + transformObj[attr].join(',') + ')');
}
return transformStrings.join(',');
}
Updated fiddle

How to avoid event's lag with KineticJS?

I am in the prototyping stage of making a 2d map builder for a hybrid web/text adventure game and so far KineticJS seems like an ideal fit. Only issue currently is that given enough velocity on mouse movement, it will skip over cells and never fire their mouseover event handler.
2 core functionality goals: When the user highlights a cell, it would be marked as "active". Additionally if they're holding the mouse down and move across the grid, cells would be flipped on or off ( this maybe refactored to set all on if not if the first cell is not active, or vice versa ).
My question: Is there a way to ensure all cells are triggered, regardless of mouse cursor velocity? If not, is there a better way to draw a line over the cells so that it consistently triggers all relevant cells?
The entire prototype has been put into jsFiddle ( http://jsfiddle.net/7ggS4/ ) but for future sake, the rest will be copied below as well.
<head>
<title>KineticJS</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/kineticjs/4.7.2/kinetic.min.js"></script>
</head>
<body>
<div id="canvas"></div>
</body>
<script defer="defer">
/**
Return's a KS layer
*/
function Grid(cells, stage) {
//Constants
// Illrelevant comment - It seriously pisses me off that canvas uses
// string color codes ( either name or string hexidecimal ) instead of taking an
// integer or actual hexidecimal 0xFFFF values. This just seems painfully inefficient.
this.activeCellColor = "green";
this.clearCellColor = "blue";
this.highlightCellColor = "red";
this.cells = cells,
this.layer = new Kinetic.Layer(),
this.grid = new Array(),
this.isMouseDown = false,
this.mouseLeft = false,
this.mouseRight = false,
this.adjRow = stage.getWidth() / cells,
this.adjCol = stage.getHeight() / cells;
this.generate();
stage.add(this.layer)
}
Grid.prototype.generate = function(){
var i, rx, ry, rect;
for (i = 0; i < this.cells * this.cells; i++) {
rx = Math.floor(i / this.cells) * this.adjRow;
ry = (i % this.cells) * this.adjCol;
rect = new Kinetic.Rect({
x: rx,
y: ry,
width: this.adjRow,
height: this.adjCol,
fill: this.clearCellColor,
stroke: 'black',
strokeWidth: .2,
cell: {x: Math.floor(i / this.cells), y: i % this.cells},
active: false,
grid: this //Just in case .bind(this) doesn't work right
});
rect.on('mouseenter', this.onMouseEnter.bind(this));
rect.on('mouseleave', this.onMouseLeave.bind(this));
rect.on('mousedown', this.onMouseDown.bind(this));
rect.on('mouseup', this.onMouseUp.bind(this));
this.grid.push(rect);
this.layer.add(rect);
}
}
Grid.prototype.onMouseEnter = function(evt) {
var src = evt.targetNode;
console.log(evt.type, this.isMouseDown, src.attrs.cell)
if (this.isMouseDown == true) {
src.attrs.active = ! src.attrs.active;
}
if (src.attrs.active == false) {
src.setFill(this.highlightCellColor);
} else {
src.setFill(this.activeCellColor);
}
this.layer.batchDraw();
}
Grid.prototype.onMouseLeave = function(evt) {
var src = evt.targetNode;
console.log(evt.type, this.isMouseDown, src.attrs.cell)
if (src.attrs.active == false) {
src.setFill(this.clearCellColor);
this.layer.batchDraw();
}
}
Grid.prototype.onMouseUp = function(evt){
var src = evt.targetNode;
console.log(evt.type, this.isMouseDown, src.attrs.cell)
this.isMouseDown = false;
}
Grid.prototype.onMouseDown = function(evt){
var src = evt.targetNode;
console.log(evt.type, this.isMouseDown, src.attrs.cell)
this.isMouseDown = true;
src.attrs.active = ! src.attrs.active;
if (src.attrs.active) {
src.setFill(this.activeCellColor);
} else {
src.setFill(this.clearCellColor);
}
this.layer.batchDraw();
}
var stage = new Kinetic.Stage({
container: 'canvas',
width: 600,
height: 600
}),
myGrid = new Grid(50, stage);
</script>
50x50=2500 active objects: that's too many for Kinetic to handle.
Remember that each "intelligent" Kinetic cell has a lot of overhead associated with it.
How about reducing your grid to 20x20?
Alternatively, you will have to separate the mouse handling from the cell handling to gain the required performance.
Mouse Handling
Your mouse handling would only involve capturing mouse points into an array of accumulated points. You can use this kind of code to capture mouse points on the stage:
$(stage.getContent()).on('click', function (event) {
myPointsArray.push(stage.getMousePosition());
});
Cell processing
The cell processing would involve applying those accumulated points to affect your grid cells. An effective place to do this code would be in a requestAnimationFrame (RAF) loop. You won't be doing animations, but RAF gives high performance because it is aware of the availability of system resources. An RAF loop would look like this:
function processPointsArray(array){
// request another loop even before we're done with this one
requestAnimationFrame(processPointsArray);
// process the points array and affect your cells here
}
A processing efficiency
RAF is called up to 60 times per second, so your user will probably navagate only a small portion of your grid during that time. You can increase performance by calculating the min/max x and y coordinates in the accumulated points array and only process those grid cells within that boundary.

Categories