Overlapping image inside a table - javascript

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;

Related

pts.js: Speed up animation of grid cells?

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.)

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 do I position modules to display like Pinterest?

I'm building a Pinterest type board plugin with jQuery, where I'm having difficulty figuring out how they position their modules.
Below is a snippet of how each element is being placed per their nth-value in the HTML. But this isn't right because Pinterest positions elements that go to the next row underneath the shortest column. Quite like this pen here, http://codepen.io/nikhilkumar80/pen/oxpXVK, but I found it difficult to understand.
function modPosition() {
$this.find(".pinterest-board__mod").each(function( modIndex ) {
$(this).css({
position: "absolute",
left: columnWidth * (modIndex % settings.columns) + "%",
width: columnWidth + "%"
});
// ..........
});
}
modPosition();
Here's my CodePen link, http://codepen.io/joshuawaheed/pen/beeJKq?editors=0010
I'm also having difficulties figuring out how to set the elements top position.
What can I do to make this work? I've put this in a function so that the positioning can run it again on document resize and when a user clicks on a filter option to remove the elements and append the appropriate modules from the appropriate array. The plugin is also set to determine module widths based on the options columns value.
Thank you in advance.
You can implement it in several ways
I suggest you two ways
1) you can use one of the js modules
You can read more about it there
https://designshack.net/articles/css/masonry/
http://www.wtfdiary.com/2012/08/6-amazing-jquery-plugins-to-design.html
2) You can use css rules(flexbox)
You can read more about it there
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
Both of these methods have positive and negative traits
For example fleksboks is not supported by all versions of the browser
But JS is more load the processor
I've got it working. I solved this by:
Creating an array, its lengt equal to the column count.
Storing the height value of modules in the first row in the array.
Looping through each module.
Injecting the smallest array value as css position top for the current module.
Adding the height of the current module with the array item containing the smallest value.
Setting the position left value by dividing 100% with the index of of the smallest value in the array.
Here is the code I wrote, which you can view and fork by following this link
function modPosition() {
var columnsHeight = [/* Length equal to column count */], // This will be recreated on window resize.
columnCount = /* Column count value */;
/* Set CSS position top and left. */
function modCssTopLeft() {
var columnIndex = 0;
$this.find(".pinterest-board__mod").each(function( modIndex ) {
var topPos = 0,
leftPos = 0;
if ( modIndex >= columnCount) {
topPos = Math.min.apply( Math, columnsHeight ); // Get smallest value in array.
leftPos = 100 * columnsHeight.indexOf(topPos) / columnCount; // Set left position based on column count.
columnsHeight[columnsHeight.indexOf(topPos)] += $(this).outerHeight(); // Change arrays smallest value by adding it with current modules height.
}
else {
leftPos = 100 * (modIndex++) / columnCount; // Positioning for the modules in the first row.
}
$(this).css({
position: "absolute",
top: topPos + "px",
left: leftPos + "%"
});
$(this).closest(".pinterest-board__content").css({
height: Math.max.apply( Math, columnsHeight ) // Set height to the modules parent container.
});
});
}
modCssTopLeft();
}
modPosition();
$(window).resize(function() {
modPosition();
});

JS/jQuery fit all images inside div (without whitespace)

I have a div call it #container,
Inside this #container I have n amount of img tags call it images
n can be 2, 10, 40 and so on.
I am wondering how I can fit n amount of images inside a #container to close all white spaces stretch the images. Quality doesn't matter
This is what I tried until now:
var amount = $("#container > img").length;
var amount_w = amount*200; //200 width of 1 image
var amount_h = amount*130; //120 height image
var refH = $("#container").height();
var refW = $("#container").width();
var refRatio = refW/refH;
$("#container img").each(function(){
$(this).height((amount_h-130)-refH);
$(this).width((amount_w-230)-refW);
});
First of all, it IS possible to achieve what you need even while maintaining the aspect ratio of the images - however the row height will be calculated, but it is not a trivial task (well, at least not as trivial as a single line formula).
There is a jQuery plugin called jPictura which I have developed. I believe the plugin does exactly what you need.
Here is a working fiddle.
You can find the plugin source codes and documentation on GitHub.
Simple example how to use the plugin:
$(document).ready(function () {
$('#my-gallery').jpictura({
layout: { itemSpacing: 0, justifyLastRow: true, idealRowHeight: 200}
});
});
itemSpacing - amount of space between the images in pixels
justifyLastRow - if true, the images in last row will be stretched to take the full width of the row
idealRowHeight - The desired height of the rows in pixels. The plugin will do its best to arrange the items so the row heights are as close as possible to this value.
there are a lot more options documented on GitHub
Beside the JS stuff that calculates the correct widths and heights of the images, there is one more thing to be considered regarding the blank space between images. Images are by default inline-blocks which means they behave like words and words do have some white space inbetween, right? Make them display: block; float: left; or use the flex box layout to get rid of the blank space. The plugin uses float: left; by default.
I created something that might interest you
var container = $('#container');
var height = container.outerHeight();
var width = container.outerWidth();
function populate(n){
var rem_items = n;
var rows = Math.round(Math.sqrt(n));
var row_items = Math.ceil(n/rows);
for (var i=0; i<rows; i++){
// this prevents us from generating a lonely single box in a row
if( (rem_items%(rows-i))===0 ){
row_items = rem_items/(rows-i);
}
if(rem_items<row_items){
row_items = rem_items;
}
rem_items = rem_items-row_items;
for (var j=0; j<row_items; j++){
var img_height = height/rows;
var img_width = width/row_items;
var img_left = j*img_width;
var img_top = i*img_height;
var img = $('<div class="cell"></div>');
img.css({
width: img_width,
height: img_height,
left: img_left,
top: img_top
});
container.append(img);
}
}
}
populate(40);
https://jsfiddle.net/jLq4hgaa/1/
Basically, it calculates the "most balanced" distribution of the images horizontally and vertically.
It does what you're asking for in the plainest sense. It distributes images/containers inside a container evenly regardless of aspect ratio.
$(document).on("pageload",function(){
$('.container').addClass('stretch');
});
Then make a css element called "stretch" defining width:100%
Height:100% and if need be define layout, i.e relative

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