Html5 canvas issue, image getting wrong size - jquery-plugins

I'm trying to create a grid of 10x10 images. These are loaded from javascript (object) and use the onload method to add it to canvas. The adding works, however the canvas images seem to be resized. The result of the currentSizes.cellSize is 80 by the way. However it seems the images added are larger than the 80 defined, even though the drawImage parameters width and height get values 80..
My goals is to built a canvas game as a jQuery plugin. I want to display a javascript-dynamically created grid with islands (just like the web browser conquest type of games). So I want to create a 100% width (and height) canvas where the javascript dynamically measures how big each cell (10x10) has to be..
So I have the following:
for( var i = 0 ; i < totalTiles ; i++ ) {
// go to first column in next row
if( x >= 10 ) {
x = x - 10;
y = y + 1;
}
// build the world
tiles[i] = new Image();
tiles[i].x = x;
tiles[i].y = y;
tiles[i].towidth = currentSizes.cellSize;
tiles[i].toheight = currentSizes.cellSize;
tiles[i].coordx = ( x * currentSizes.cellSize );
tiles[i].coordy = ( y * currentSizes.cellSize );
//tiles[i].onload = function() { canvas2d.drawImage( tiles[i] , 0 , 0 ); }
//tiles[i].onLoad = dibaMethods.drawTile( tiles[i] , ( x * currentSizes.cellSize ) , ( y * currentSizes.cellSize ) , canvas2d );
tiles[i].src = "/images/world/tiles/card5.png";
lastIndex = i;
console.log( "looping at: " + i + " with x/y: " + x + "/" + y + " and coordinates: " + tiles[i].coordx + "/" + tiles[i].coordy );
x++;
}
console.log( "Canvas tiles entered, loaded on DOM load, tiles added: " + tiles.length );
tiles[lastIndex].onload = function() {
for( var i = 0; i < totalTiles ; i++ ) {
canvas2d.drawImage( tiles[i] , tiles[i].coordx , tiles[i].coordy );
//canvas2d.drawImage( tiles[i] , tiles[i].coordx , tiles[i].coordy , tiles[i].towidth , tiles[i].toheight );
}
}
The HTML of the canvas:
<canvas id="world">
</canvas>
Stylesheet:
body { margin: 0; }
canvas#world { clear: both; background: url("/images/world/clouds.jpg") no-repeat; width: 800px; height: 800px; }
You can see how far i got on the url: https://battles.hyn.me/world
Any advice is much appreciated! If the question needs elaborating, please comment!

The issue originated from a canvas element which did not have a width or height attribute.
After editing the html to include these attributes and removing the stylesheet entries it finally worked.
So bottomline, always add a width and height attribute to the canvas element.

Related

How do you achieve text scrolling inside a canvas

I have the code below which renders a text on canvas, that refracts to the 3D object, I want to make the text scroll with the mouse wheel either using translate but cannot seem to make it bind, is there a way to achieve that?
I have done word wrap with lineheight and make the font text not blurry on HD Monitors on the different functions but cannot seem to achieve the scrolling only, any help would be much appreciated thank you.
function printAtWordWrap(context, text, x, y, lineHeight, fitWidth) {
fitWidth = fitWidth || 0;
if (fitWidth <= 0) {
context.fillText(text, x, y);
return;
}
var words = text.split(' ');
var currentLine = 0;
var idx = 1;
while (words.length > 0 && idx <= words.length) {
var str = words.slice(0, idx).join(' ');
var w = context.measureText(str).width;
if (w > fitWidth) {
if (idx == 1) {
idx = 2;
}
context.fillText(words.slice(0, idx - 1).join(' '), x, y + (lineHeight * currentLine));
currentLine++;
words = words.splice(idx - 1);
idx = 1;
} else {
idx++;
}
}
if (idx > 0)
context.fillText(words.join(' '), x, y + (lineHeight * currentLine));
}
var PIXEL_RATIO = (function() {
var ctx = document.createElement("canvas").getContext("2d"),
dpr = window.devicePixelRatio || 1,
bsr = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1;
return dpr / bsr;
})();
let can;
function createHiDPICanvas(w, h, ratio) {
if (!ratio) {
ratio = PIXEL_RATIO;
}
can = document.createElement('canvas');
can.width = w * ratio;
can.height = h * ratio;
can.style.width = w + "px";
can.style.height = h + "px";
can.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0);
return can;
}
//Create canvas with a custom resolution.
if (window.devicePixelRatio > 1) {
var myCanvas = createHiDPICanvas(window.innerWidth, window.innerHeight, 4);
} else {
var myCanvas = createHiDPICanvas(window.innerWidth, window.innerHeight, 2);
}
var ctx = myCanvas.getContext('2d', {
alpha: false,
antialias: false
});
let x = window.innerWidth / 2;
var y = window.innerHeight / 2;
ctx.translate(0, 200);
const font = new FontFace("MyCanvasFont", "url(https://use.typekit.net/af/2759ad/00000000000000007735a2d2/30/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n3&v=3)");
document.fonts.add(font);
ctx.font = '300 150px "MyCanvasFont"';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
var txt = 'CONNECTING BRANDS TO HUMANS THROUGH CULTURE, DESIGN AND TECHNOLOGY';
printAtWordWrap(ctx, txt, x, y, 120, window.innerWidth / 2);
The main point is: You need know how the first line to show. You can use a param to control this.
function printAtWordWrap(context, text, x, y, lineHeight, fitWidth, line)
control what line can show:
if(currentLine >= line){
//remove line value of currentline
context.fillText(words.slice(0, idx - 1).join(' '), x, y + (lineHeight * (currentLine - line) )); }
Note that ai remove line value of currentLin to up in canvas position.
Look my project: https://codepen.io/Luis4raujo/pen/xxRrmoE
If you can up lines instead hide, you need remove if statement (line 24)
If this answer help you, vote or check as correnct!

SVG.js - how do I move and rotate an element to follow the path so that the element always maintain a heading

I am using SVG.js and jquery to develop a simple yacht racing SVG animation to simulate a yacht race. I can easily animate an image [yacht icon] along the path. But I also want the yacht to rotate or the yacht heading change as the yacht travels up the path/route. I have tried a view things but cannot get it working. please help!
i am using this code..
function move_yacht_new(route,yacht,score){
var route1 = draw.path('M 535.07931,510.0164 C 594.9314,509.34072 667.14382,469.54596 691.80372,382.84271');
route1.fill('none');
var length = route1.length();
var y_pos = score;
var sailAngle = 90;
var currentRotation;
route1.hide();
var text = draw.text(function(add) {
add.tspan('We go ')
add.tspan('up').fill('#f09').dy(-40)
add.tspan(', then we go down, then up again').dy(40)
})
var image = draw.image('../globals/game/images/' + yacht + '-yacht.png',20, 20).id('yacht_' + yacht);
var scorePerc = (length/100) * (score);
//vdiscountPerc = calcPerc.toFixed();
var text = draw.text(yacht + ' (' + Math.round(score) + ')');
image.animate(5000, '<>').during(function(pos, morph, eased){
var p = route1.pointAt(eased * scorePerc);
image.move(p.x, p.y);
text.move(p.x, p.y).font({ fill: '#fff', size: 5, family: 'Helvetica', leading: '1.5em' });
var coord = image.bbox();
var center = coord.x + (coord.width/2) + ' '+ coord.y + (coord.height/2);
console.log('center ' + center);
var x_coord = image.attr('x');
var y_coord = image.attr('y');
//console.log(x_coord + ',' + y_coord);
image.rotate(-45, coord.x + (coord.width/2), coord.y + (coord.height/2));
var angle = Math.atan2(p.y, p.x) * 180 / Math.PI;//angle for tangent
//Shifting center to center of rocket
var centerX = p.x - 24,
centerY = p.y - 12;
console.log('angle > ' + angle);
//}).loop(true, true)
})
}
Blockquote
But this rotates the icons away from the path in a wrong direction.
I managed to resolve this using animation.js script.
Add the yacht icon into a group, translate the group along the path and rotate the yacht icon in the group around the group origin:
var g = draw.group()
var yacht = g.image(yachtUrl, width, height).center(0,0)
// in animate function:
g.transform(x: x, y: y)
yacht.transform(rotation: degrees, cx: 0, cy: 0)

How to explode a 3D model group in ThreeJs?

I have a 3D model , containing many child meshes.
I want to "explode" that mesh ... meaning that every child mesh has to move away from a central point exactly like that thread :
Exploded view algorithm for CAD
To do that , I generate a bounding box of the ancestor mesh , with THREE.Box3 , and compute the center with .getCenter() .
The issue here comes from threeJS's scene graph ...
when setting the position of each child , the whole 3D model does explode when I set it to do so in all directions (XYZ) , but when I choose other combinations like X alone , the explode either bugs and spreads in all directions when it shouldn't, or doesn't explode at all .
Here is the code I am using:
/**
* obj : the current node on the scene graph
* box_ct_world : a vec3 center of the bounding box
*
*/
explode : function(obj , parent , box_ct_world , dif){
var scene = this.el.sceneEl.object3D ; //I am using Aframe , so this is how I retrieve the whole scene .
var object = this.el.object3D ; //same here , this for retrieving the whole 3D model .
var speed = 1 ;
if(obj instanceof THREE.Mesh){
var box_center = box_ct_world ;
var position = obj.position ;
/**
* so this is the beginning of my troubles : I am considering retrieving a world position instead of a local position...
* I have to translate the nodes without the parent matrices interfering .
* The current ligne works the same way as if I retrieved local transformations ... exploding on XYZ , but glitching on the other cases .
*/
position.setFromMatrixPosition(scene.matrixWorld) ;
var addx =0 ;
var addy =0 ;
var addz =0 ;
/**
* This is the vector from the center of the box to the node . we use that to translate every meshes away from the center
*/
if(this.data.X === true){
var addx =(position.x - box_center.x)*this.data.factor *speed ;
}
if(this.data.Y === true){
var addy =(position.y - box_center.y)*this.data.factor *speed;
}
if(this.data.Z === true){
var addz =(position.z - box_center.z)*this.data.factor *speed;
}
var explode_vectorx= addx;
var explode_vectory= addy;
var explode_vectorz= addz;
/**
* this is for making the nodes translate back to their original locations
*/
if(diff < 0 ){
if(explode_vectorx > 0)
explode_vectorx = -explode_vectorx ;
if(explode_vectory > 0)
explode_vectory = -explode_vectory;
if(explode_vectorz > 0)
explode_vectorz = -explode_vectorz;
}
if(diff > 0 ){
if(explode_vectorx < 0)
explode_vectorx = -explode_vectorx ;
if(explode_vectory < 0)
explode_vectory = -explode_vectory;
if(explode_vectorz < 0)
explode_vectorz = -explode_vectorz;
}
var vector = new THREE.Vector3(explode_vectorx , explode_vectory, explode_vectorz) ;
console.log(vector.x+" " + vector.y+ " " + vector.z );
/**
* and here is my biggest problem :
* this function seems to use ancestors matrices
* I need the nodes to move without calling the ancestors matrices , but still keep their original rotation and scale .
*/
obj.position.set(vector.x , vector.y , vector.z ) ;
if(obj.children.length != 0 ){
for(var i = 0 ; i < obj.children.length ; i++){
this.explode(obj.children[i] ,obj, box_ct_world , dif);
}
}
}
else{
if(obj.children.length != 0 )
{
for(var i = 0 ; i < obj.children.length ; i++){
this.explode(obj.children[i] ,obj, box_ct_world , dif);
}
}
}
},
so here is a screenshot using a XYZ explode :
It works pretty fine .
But when using a single axis , like X :
the mesh still goes on all axis as if it was random, when it is supposed to translate only on the X axis .
I think this could be fixed by translating the meshes directly in world space , but I don't know how that can be done in threeJS , especially in that case , because I cannot change the values in matrixWorld (A frame is updating the world matrix every frame I think for these objects , so even if I change any value in the matrixWorld , these will be overriden)
thank you all for your help.

html5 canvas, how to export a 2x image?

I designed a web app with html5 canvas. To export an image, the code will be below:
var img = canvas.toDataURL("image/png");
Is there any way to export a 2x image?
It is for hdpi display like apple retina display.
Yes there are a few ways but every time you stretch a non vector image you will get some pixel distortion. However if its only two times the size you could get away with it using nearest neighbor. The below example shows two different methods, one is just stretching the image, the other uses nearest neighbor with a zoom factor of two.
Live Demo
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
canvas2 = document.getElementById("canvas2"),
ctx2 = canvas2.getContext("2d"),
tempCtx = document.createElement('canvas').getContext('2d'),
img = document.getElementById("testimg"),
zoom = 2;
tempCtx.drawImage(img, 0, 0);
var imgData = tempCtx.getImageData(0, 0, img.width, img.height).data;
canvas.width = img.width * zoom;
canvas.height = img.height * zoom;;
// nearest neighbor
for (var x = 0; x < img.width; ++x) {
for (var y = 0; y < img.height; ++y) {
var i = (y * img.width + x) * 4;
var r = imgData[i];
var g = imgData[i + 1];
var b = imgData[i + 2];
var a = imgData[i + 3];
ctx.fillStyle = "rgba(" + r + "," + g + "," + b + "," + (a / 255) + ")";
ctx.fillRect(x * zoom, y * zoom, zoom, zoom);
}
}
// stretched
ctx2.drawImage(img, 0, 0, 140, 140);
#phrogz has a great example of this here as well, showing a few different ways you can accomplish image re-sizing.

How do you scale n images to fit a certain width?

I've got 3 images I want to fit on a web page horizontally side by side, they are of various proportions and I want them to end up sharing a particular height (to be calculated). So let's say the width of my page is 't' and the current dimensions of the images are h1 x w1, h2 x w2, h3 x w3
I worked out a formula for 2 images but I can't get my head around 3 or more:
(h1*h2*t) / (w1*h2 + h1*w2)
The condition you must respect is:
k1*w1 + k2*w2 + ... + kn*wn = t
where kn is the scaling constant applied to the width to keep the original proportion of the image with its new height.
We can say that
kn = h_new / hn
where h_new is the new height for all images. From there it's all substitution and isolation
h_new*w1/h1 + h_new*w2/h2 + ... + h_new*wn/hn = t
h_new * (w1/h1 + w2/h2 + ... + wn/hn) = t
h_new = t / (w1/h1 + w2/h2 + ... + wn/hn)
I think that should be it, reply if I'm completely wrong! :)
I wrote a photoshop CS5 script in javascript to resize and save out the open images based on #vache's formula. Hope someone finds it useful:
var outputFolder = Folder.selectDialog("Select a folder for the output files")
if(outputFolder != null) {
var startRulerUnits = app.preferences.rulerUnits
var startDisplayDialogs = app.displayDialogs
// Set Adobe Photoshop CS5 to use pixels and display no dialogs
app.preferences.rulerUnits = Units.PIXELS
app.displayDialogs = DialogModes.NO
do {
var totalWidth = parseInt( prompt("How wide do they need to fit into?", 844) );
}
while(totalWidth <= 0 || isNaN(totalWidth));
var DL = documents.length;
var totalArea = 0;
for(a=0;a<DL;a++){
var cur = documents[a];
totalArea += cur.width / cur.height;
}
var newHeight = totalWidth / totalArea;
for(a=1;a<=DL;a++){
activeDocument = documents[a-1];
var AD=activeDocument;
// bring to front
app.activeDocument = AD;
AD.changeMode(ChangeMode.RGB);
var imgName= AD.name.toLowerCase();
imgName = imgName.substr(0, imgName.length -4);
AD.flatten();
AD.resizeImage(null,UnitValue(newHeight,"px"),null,ResampleMethod.BICUBIC);
//AD.resizeImage(UnitValue(newWidth,"px"),null,null,ResampleMethod.BICUBIC);
saveForWeb(outputFolder, imgName, AD);
}
// Close all the open documents
while (app.documents.length) {
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES)
}
// Reset the application preferences
app.preferences.rulerUnits = startRulerUnits;
app.displayDialogs = startDisplayDialogs;
}
function saveForWeb(outputFolderStr, filename, AD)
{
var opts, file;
opts = new ExportOptionsSaveForWeb();
opts.format = SaveDocumentType.JPEG;
opts.quality = 80;
if (filename.length > 27) {
file = new File(outputFolderStr + "/temp.jpg");
AD.exportDocument(file, ExportType.SAVEFORWEB, opts);
file.rename(filename + ".jpg");
}
else {
file = new File(outputFolderStr + "/" + filename + ".jpg");
AD.exportDocument(file, ExportType.SAVEFORWEB, opts);
}
}

Resources