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

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);
}
}

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!

pixijs very slow in mobile compared to css

I'm testing PIXIjs for a simple 2D graphics, basically I'm sliding tiles with some background color and borders animation, plus I'm masking some parts of the layout.
While it works great in desktops it's really slower than the same slide+animations made with pure css in mobile devices (where by the way I'm using crosswalk+cordova so the browser is always the same)
For moving tiles and animating color I'm calling requestAnimationFrame for each tile and I've disabled PIXI's ticker:
ticker.autoStart = false;
ticker.stop();
This slowness could be due to a weaker GPU on mobiles? or is just about the way I use PIXI?
I'm not showing the full code because is quite long ~ 800 lines.
The following is the routine I use for each tile once a slide is captured:
const animateTileBorderAndText = (tileObj, steps, _color, radius, textSize, strokeThickness, _config) => {
let pixiTile = tileObj.tile;
let s = 0;
let graphicsData = pixiTile.graphicsData[0];
let shape = graphicsData.shape;
let textStyle = pixiTile.children[0].style;
let textInc = (textSize - textStyle.fontSize) / steps;
let strokeInc = (strokeThickness - textStyle.strokeThickness) / steps;
let prevColor = graphicsData.fillColor;
let color = _color !== null ? _color : prevColor;
let alpha = pixiTile.alpha;
let h = shape.height;
let w = shape.width;
let rad = shape.radius;
let radiusInc = (radius - rad) / steps;
let r = (prevColor & 0xFF0000) >> 16;
let g = (prevColor & 0x00FF00) >> 8;
let b = prevColor & 0x0000FF;
let rc = (color & 0xFF0000) >> 16;
let rg = (color & 0x00FF00) >> 8;
let rb = color & 0x0000FF;
let redStep = (rc - r) / steps;
let greenStep = (rg - g) / steps;
let blueStep = (rb - b) / steps;
let paintColor = prevColor;
let goPaint = color !== prevColor;
let animate = (t) => {
if (s === steps) {
textStyle.fontSize = textSize;
textStyle.strokeThickness = strokeThickness;
//pixiTile.tint = color;
if (!_config.SEMAPHORES.slide) {
_config.SEMAPHORES.slide = true;
PUBSUB.publish(_config.SLIDE_CODE, _config.torusModel.getData());
}
return true;
}
if (goPaint) {
r += redStep;
g += greenStep;
b += blueStep;
paintColor = (r << 16) + (g << 8) + b;
}
textStyle.fontSize += textInc;
textStyle.strokeThickness += strokeInc;
pixiTile.clear()
pixiTile.beginFill(paintColor, alpha)
pixiTile.drawRoundedRect(0, 0, h, w, rad + radiusInc * (s + 1))
pixiTile.endFill();
s++;
return requestAnimationFrame(animate);
};
return animate();
};
the above function is called after the following one, which is called for each tile to make it slide.
const slideSingleTile = (tileObj, delta, axe, conf, SEM, tilesMap) => {
let tile = tileObj.tile;
let steps = conf.animationSteps;
SEM.slide = false;
let s = 0;
let stepDelta = delta / steps;
let endPos = tile[axe] + delta;
let slide = (time) => {
if (s === steps) {
tile[axe] = endPos;
tileObj.resetPosition();
tilesMap[tileObj.row][tileObj.col] = tileObj;
return tileObj.onSlideEnd(axe == 'x' ? 0 : 2);
}
tile[axe] += stepDelta;
s++;
return requestAnimationFrame(slide);
};
return slide();
};
For each finger gesture a single column/row (of NxM matrix of tiles) is slided and animated using the above two functions.
It's the first time I use canvas.
I red that canvas is way faster then DOM animations and I red very good review of PIXIjs, so I believe I'm doing something wrong.
Can someone help?
In the end I'm a complete donk...
The issue is not with pixijs.
Basically I was forcing 60fps! The number of steps to complete the animation is set to 12 that implies 200ms animation at 60FPS (using requestAnimationFrame) but in low end devices its going to be obviously slower.
Css animation works with timing as parameter so it auto adapt FPS to devices hardware.
To solve the issue I'm adapting the number of steps during animations, basically if animations takes longer than 200ms I just reduce number of steps proportionally.
I hope this could be of help for each web developer used to css animation who have just started developing canvas.

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 rotate image inside HTML5 canvas without drawing it?

I'm generating image programmatically inside canvas.
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
// here I have some code in loop setting individual pixels
// ...
//
// save image to variable
var dataURL = canvas.toDataURL();
How can I rotate created image by 90 degrees?
EDIT:
This is not duplicate because I don't draw image, it is never visible. I only want to generate it, rotate it and save to variable.
EDIT2:
I'm trying to rotate it with this code:
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(90 * Math.PI / 180)
But it doesn't work
EDIT3:
This is more complex example of my code:
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
canvas.setPixel = function (x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x, y, 1, 1);
}
for (var i in data) {
for (var j in data[i]) {
switch (data[i][j]) {
case 1:
var color = '#ffff00',
type = 'w'
break
case 3:
var rgb = (256 - parseInt(pixels[i][j]) - minus.grass).toString(16),
color = '#00' + rgb + '00',
type = 'g'
break
case 4:
var rgb = (256 - parseInt(pixels[i][j]) - minus.hills).toString(16),
color = '#' + rgb + rgb + '00',
type = 'h'
break
case 5:
var rgb = (parseInt(pixels[i][j]) + minus.mountains).toString(16),
color = '#' + rgb + rgb + rgb,
type = 'm'
break
case 6:
var rgb = (parseInt(pixels[i][j]) + minus.snow).toString(16),
color = '#' + rgb + rgb + rgb,
type = 'm'
break
}
if (i % fieldSize == 0 && j % fieldSize == 0) {
if (notSet(fields[y])) {
fields[y] = []
}
fields[y][x] = type
x++
}
canvas.setPixel(i, j, color)
}
if (i % fieldSize == 0) {
x = 0
y++
}
}
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(90 * Math.PI / 180)
var token = {
type: 'save',
map: canvas.toDataURL('image/png')
}
ws.send(JSON.stringify(token))
To rotate image by 90 degrees I had to put
ctx.translate(0, canvas.height)
ctx.rotate(270 * Math.PI / 180)
before
for (var i in data) {
for (var j in data[i]) {
switch (data[i][j]) {
// ... drawing pixels
}
}
}

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.

Resources