Resizing camera display in Seriously.js - javascript

I'm trying to resize the camera display within the canvas DOM element. I've tried using an event listener and am able to resize the canvas without any problem. The camera's video feed however is not updating its size, t stays the same as it was upon initialization. In order to make it work I've had to basically destroy much of the composition and then re-initialize the whole thing every time a window resize occurs. It works but its ugly. Does anyone have a better solution in mind? Maybe there is a way to make this one more elegant? Thanks for any input.
window.addEventListener( 'resize', onWindowResize, false );
function onWindowResize() {
// seriously
reformat = null
source = null
target.destroy()
target = null
seriously = new Seriously();
canvas.width = window.innerWidth * devicePixelRatio;
canvas.height = window.innerHeight * devicePixelRatio;
var source = seriously.source('camera');
target = seriously.target('#canvas');
reformat = seriously.transform('reformat');
reformat.source = source;
reformat.width = canvas.width;
reformat.height = canvas.height;
target.source = reformat
seriously.go(function(now) {
var minutes;
minutes = now / 6000;
console.log("running")
// split.angle = (minutes - Math.floor(minutes)) * PI2;
// split.split = Math.cos(now / 1000) / 2 + 0.5;
});

After iterating through each of the Seriously examples I found a simple solution.
var devicePixelRatio = window.devicePixelRatio || 1;
function resize() {
target.width = window.innerWidth * devicePixelRatio;
target.height = window.innerHeight * devicePixelRatio;
reformat.width = target.width;
reformat.height = target.height;
}
window.onresize = resize;

Related

I want smooth animation on live server for image sequence scroll

I'm using canvas for this animation. The animation is working fine on localhost but on the live servers it's taking too much time.
This is because I'm using almost 3000 frames for this animation, all frames are important. How can I increase the loading speed on the live server?
I have attached the code. Please review it and help me if I'm wrong somewhere.
const html = document.documentElement;
const canvas = document.getElementById("hero-lightpass");
const context = canvas.getContext("2d");
const frameCount = 2999;
const currentFrame = index => (`compressed/${index.toString().padStart(9, '720_0000')}.jpg`)
const preloadImages = () => {
for (let i = 1; i < frameCount; i++) {
const img = new Image();
img.src = currentFrame(i);
}
};
const img = new Image()
img.src = currentFrame(1);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
img.onload = function() {
scaleToFill(this);
}
function scaleToFill(img) {
var scale = Math.max(canvas.width / img.width, canvas.height / img.height);
var x = (canvas.width / 2) - (img.width / 2) * scale;
var y = (canvas.height / 2) - (img.height / 2) * scale;
context.drawImage(img, x, y, img.width * scale, img.height * scale);
}
const updateImage = index => {
img.src = currentFrame(index);
context.drawImage(img, 0, 0);
}
window.addEventListener('scroll', () => {
const scrollTop = html.scrollTop;
const maxScrollTop = html.scrollHeight - window.innerHeight;
const scrollFraction = scrollTop / maxScrollTop;
const frameIndex = Math.min(
frameCount - 1,
Math.ceil(scrollFraction * frameCount)
);
requestAnimationFrame(() => updateImage(frameIndex + 1))
});
preloadImages()
<canvas id="hero-lightpass"></canvas>
There might be some edge cases where you still need individual images, and the suggestions in the comments (using video or Sprite sheets) will not do, if you can use one of those option you should, it will simplify a lot...
I'm going to focus on that edge case where we have a ton of images
Great examples how to do things like that are online maps:
Google maps (https://www.google.com/maps)
OpenStreetMap (https://www.openstreetmap.org/)
Those maps have a lot of images, but they do not download all at once, they download and draw only what is needed, in your case you can preload maybe 50 (you have to experiment to see what best) and download the 51, 52 ... as you are drawing the first you have preloaded.
Back to the maps; They use "small" map tiles to paint one big mosaic, here are a couple of tiles:
https://a.tile.openstreetmap.org/6/16/25.png
https://b.tile.openstreetmap.org/6/15/26.png
You can see they are coming from different servers a & b that is to speed up download, browsers have limits on how many images are downloaded from one server at a time, for more details see:
http://kb.mozillazine.org/Network.http.max-connections-per-server
In a nutshell:
Download and draw only what is needed
Use multiple servers to host your images

Is there a way to avoid letterboxing with canvas scaling?

I'm working on a small project in Javascript, using Pixi.js as the rendering engine. However, I've only found a few methods of scaling the canvas to full window that seem to work best for the current version. It does have a caveat, however, in that it produces letterboxes on the sides based on the orientation.
Is it possible to avoid the letterboxing at all with Pixi?
This is the code that I have so far, as it relates to the scaling:
var application = null;
var GAME_WIDTH = 1060;
var GAME_HEIGHT = 840;
var ratio = 0;
var stage = null;
application = new PIXI.Application(
{
width: GAME_WIDTH,
height: GAME_HEIGHT,
backgroundColor: 0x00b4f7,
view: document.getElementById("gwin")
});
stage = new PIXI.Container(true);
window.addEventListener("resize", rescaleClient);
function rescaleClient()
{
ratio = Math.min(window.innerWidth / GAME_WIDTH, window.innerHeight /
GAME_HEIGHT);
stage.scale.x = stage.scale.y = ratio;
application.renderer.resize(Math.ceil(GAME_WIDTH * ratio), Math.ceil(GAME_HEIGHT * ratio));
}
My goal with this is to achieve a similar full screen/window effect to Agar.io/Slither.io, however I have not discovered a satisfactory method that allows it easily. There do seem to be examples that use Pixi to achieve this, but the code is more then often closed source, and they seem to use external tools such as Ionic and Phonegap.
I finally found the answer. I was close to the right track, but a few more things needed to be applied.
application.renderer.view.style.position = "absolute";
application.renderer.view.style.display = "block";
application.renderer.autoResize = true;
application.renderer.resize(window.innerWidth, window.innerHeight);
This sets some additional things internally, while a minor modification to the resize script...
ratio = Math.min(window.innerWidth / GAME_WIDTH, window.innerHeight / GAME_HEIGHT);
stage.scale.x = stage.scale.y = ratio;
renderer.resize(window.innerWidth, window.innerHeight);
configures things correctly, so that the related Renderer window now fills the screen without squashing the content.
This was not easy to discover. So many tutorials just leave it at the first half, and assume that is what people wish to do.
var application;
//var GAME_WIDTH = window.screen.width-20;
var GAME_WIDTH = window.innerWidth;
//var GAME_WIDTH = document.body.clientWidth;
var GAME_HEIGHT = window.innerHeight;
var ratiox = 0;
var ratioy = 0;
var container;
application = new PIXI.Application(
{
width: GAME_WIDTH,
height: GAME_HEIGHT,
backgroundColor: 0x00b4f7,
view: document.getElementById("gwin")
});
//document.body.appendChild(application.view);
container = new PIXI.Container(true);
application.stage.addChild(container);
window.addEventListener("resize", rescaleClient);
function rescaleClient()
{
//ratiox = Math.min(window.innerWidth / GAME_WIDTH, window.innerHeight / GAME_HEIGHT);
application.stage.scale.x = ratiox = window.innerWidth / GAME_WIDTH
application.stage.scale.y = ratioy = window.innerHeight / GAME_HEIGHT;
application.renderer.resize(Math.ceil(GAME_WIDTH * ratiox), Math.ceil(GAME_HEIGHT * ratioy));
}
#viewport{
width:device-width
}
body {
padding :0px;
margin:0px
}
<script src="https://pixijs.download/v4.6.2/pixi.min.js"></script>
<canvas id="gwin"></canvas>

How can I obtain screen scaling similar to Slither.io's using Phaser.js?

I am writing a small game in Javascript using the Phaser.js engine as a backend. However, knowing it is possible, the screen scaling issue has proven to be one
of the hardest sides of this.
Slither.io expands to fill the entire screen, and keeps the ratio. I want to use that as my own scaling method as well, but examples seem to be rather sparse.
The ones I have come across have not been all that clear.
These two functions are what I have come up with thus far, but they seem to cause the sprites to match the window scale - not keep a ratio and scale.
function resizeWindow()
{
game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
game.width = window.innerWidth * window.devicePixelRatio;
game.height = window.innerHeight * window.devicePixelRatioz;
gameScale = game.width / game.height;
if (game.height > game.width){
gameScale = (game.height / 300);
} else {
gameScale = (game.width / 480);
}
//game.scale.pageAlignVertically = true;
game.scale.pageAlignHorizontally = true;
game.scale.refresh();
gameScaleManager();
}
function gameScaleManager()
{
for(var i = 0; i < spriteList.length; i++)
{
spriteList[i].scale.setTo(gameScale, gameScale);
}
}
I finally solved it by way of a number of things
First, set your game scale mode to:
game.scale.scaleMode = Phaser.ScaleManager.RESIZE;
and then apply
game.scale.pageAlignVertically = true;
This does matter, especially for full window size.
Instead of relying on jQuery for rescaling, I then set this:
game.scale.setResizeCallback(rescale);
The rescale method handles screen scaling.
Finally, this method takes care of the actual work along with the "RESIZE" scale mode, which WILL affect -how- it works. EXACT_FIT and others tend to force the resolution to be the same as the window, from the experimentation I've done.
function rescale()
{
var windowHeight = window.innerHeight;
var windowWidth = window.innerWidth;
baseWidth = windowWidth * (gameHeight / windowHeight);
baseHeight = gameHeight;
if (windowHeight > windowWidth)
{
systemScale = (windowHeight / baseHeight) / 1.8;
} else {
systemScale = (windowWidth / baseWidth) / 1.8;
}
skyBackgroundGroup.scale.setTo(systemScale * (window.innerWidth - DPR / window.innerWidth - DPR) / 100 * 1, systemScale * 2.1);
MasterControlGroup.scale.setTo(systemScale / 0.98, systemScale / 0.98);
}
The divide by scale of 1.8 and 0.98 are entirely personal preferences.
I almost forgot the biggest part of it - have your camera focus on the center object you are watching.

Weird behavior with canvas Context variable definition location causing canvas render freeze when tab is inactive

I have come across a very odd behavior that is causing issues in Chrome (only tested browser). I have a canvas that is being used to render an audio frequency spectrum visual. Whenever I define the CanvasRenderingContext2D variable
var canvasCtx;
canvasCtx = canvas.getContext("2d");
or define the ScriptProcessorNode variable
var scriptNode;
scriptNode = audioCtx.createScriptProcessor(2048, 1, 1);
and the variable definition is inside any closure or function, the visualization rendering will freeze if you switch tabs, change windows, or even change the window width size.
However, I found out that if the variable definition for those two types are defined at the global scope outside all functions, the canvas will continue to render even if the page is inactive.
Here is a JSFiddle demonstrating the bug:
http://jsfiddle.net/K8J26/541/
(Audio may not play in JSFiddle due to cross-origin safety, so a local file may be needed)
I am wondering, why is this happening?
Does the browser hold onto the state of variables and objects that are tied to the global scope even if the page is inactive? Or is it something completely unrelated to the location of variable definition, and instead something I just completely overlooked?
Thanks!
Your problem seems to be that you are calculating the new size and creating the gradient while the browser has not updated the size of your elements yet.
Pass the updateScale in a requestAnimationFrame call so the browser has the time to render the elements :
(function initVisualizer() {
var AudioContext = window.AudioContext || window.webkitAudioContext;
if (!AudioContext) {
return;
}
var PERCENT_HIGH_FREQ_TRIM = 0.3;
var PERCENT_HEIGHT_OF_WITDH = 0.25;
var SMOTHING_CONSTANT = 0.6;
var audioCtx = new AudioContext();
var canvas = document.getElementById("visualizer");
canvas.style.width = "100%";
// Defining canvasCtx here causes frozen canvas rendering when tab is inactive
var canvasCtx = canvas.getContext("2d");
// Defining scriptNode here causes frozen canvas rendering when tab is inactive
var scriptNode = audioCtx.createScriptProcessor(2048, 1, 1);
scriptNode.connect(audioCtx.destination);
var analyser = audioCtx.createAnalyser();
analyser.smoothingTimeConstant = SMOTHING_CONSTANT;
analyser.connect(scriptNode);
var widthMax;
var heightMax;
var dataCount;
var barWidth;
var barOffset;
var gradient;
scriptNode.onaudioprocess = function() {
var frequencyData = new Uint8Array(dataCount);
analyser.getByteFrequencyData(frequencyData);
canvasCtx.clearRect(0, 0, widthMax, heightMax);
canvasCtx.fillStyle = gradient;
var modifier = heightMax / 255;
for (var i = 0; i < dataCount; i++) {
var value = ~~(frequencyData[i] * modifier);
canvasCtx.fillRect(i * (barWidth + barOffset), heightMax - value, barWidth, heightMax);
}
};
window.addEventListener("resize", function() {
if (canvas.parentElement.offsetWidth !== widthMax) {
requestAnimationFrame(updateScale);
}
});
function updateScale() {
widthMax = canvas.offsetWidth;
heightMax = ~~(widthMax * PERCENT_HEIGHT_OF_WITDH);
var size = nearestPow2(~~(widthMax / 4));
dataCount = ~~((size / 2) * (1 - PERCENT_HIGH_FREQ_TRIM));
var rectWidth = widthMax / dataCount;
barOffset = rectWidth * 0.25;
barWidth = rectWidth - barOffset;
analyser.fftSize = size;
canvas.setAttribute("width", widthMax);
canvas.setAttribute("height", heightMax);
gradient = canvasCtx.createLinearGradient(0, 0, 0, heightMax);
gradient.addColorStop(1, "#ff6600");
gradient.addColorStop(0, "#ffff00");
}
function playAudio(audio) {
var sourceNode = audioCtx.createMediaElementSource(audio);
sourceNode.connect(analyser);
sourceNode.connect(audioCtx.destination);
console.log(audio.play());
setTimeout(function() {
audio.pause();
}, 30000);
}
function nearestPow2(size) {
return Math.pow(2, Math.round(Math.log(size) / Math.log(2)));
}
updateScale();
playAudio(document.getElementById('audio'));
})();
body {
background: #222;
}
<audio id="audio" controls crossorigin="anonymous">
<source src="https://upload.wikimedia.org/wikipedia/en/d/dc/Strawberry_Fields_Forever_%28Beatles_song_-_sample%29.ogg" />
<source src="https://upload.wikimedia.org/wikipedia/en/d/dc/Strawberry_Fields_Forever_%28Beatles_song_-_sample%29.ogg" />
</audio>
<canvas id="visualizer"></canvas>
However I don't know why it was working when you set the variable in global context...
Ps : your fiddle wasn't working on FF and only worked on chrome because of a bug, you need to set the crossorigin attribute before the resource are loaded, otherwise it has no effect.

How to make a snow fall on web page text?

I have this JavaScript code for snow falling effect. It's working really nice,
but I decided to create a snow falling on web page text. I have tried, but not getting how to do it.
How to make snow fall on that "I love you" text? http://jsfiddle.net/DgrxX/22/
I have already seen in VB6 code where snow fall on text. If it is possible in Visual Basic 6 seven years ago, why not today in JavaScript or CSS3 or jQuery?
function vp(woh)
{
var viewportwidth;
var viewportheight;
if (typeof window.innerWidth != 'undefined')
{
viewportwidth = window.innerWidth,
viewportheight = window.innerHeight
}
else if (typeof document.documentElement != 'undefined'
&& typeof document.documentElement.clientWidth !=
'undefined' && document.documentElement.clientWidth != 0)
{
viewportwidth = document.documentElement.clientWidth,
viewportheight = document.documentElement.clientHeight
}
else
{
viewportwidth = document.getElementsByTagName('body')[0].clientWidth,
viewportheight = document.getElementsByTagName('body')[0].clientHeight
}
if (woh == 'w')
{
return viewportwidth;
}
else if (woh == 'h')
{
return viewportheight;
}
else
{
return false;
}
}
function snowflake()
{
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.radius = Math.random()*2;
this.color = "white";
var tobefallingSpeed = Math.random() * 100;
this.fallingSpeed = tobefallingSpeed + 30;
}
function render()
{
ctx.clearRect(0,0,canvas.width, canvas.height);
for (b=0;b<snowflakes;b++)
{
sf[b].y+=0.4;
if(sf[b].y> canvas.height){
sf[b].y = 0;
}
ctx.fillStyle = "#FFFFFF";
ctx.fillRect(sf[b].x,sf[b].y,sf[b].radius,sf[b].radius);
}
}
function main()
{
now = Date.now();
delta = now - then;
render();
then = now;
}
then = Date.now();
var int = self.setInterval(main,1);
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = vp('w');
canvas.height = vp('h');
document.body.appendChild(canvas);
var numberofSnowflakes = 100;
var sf = [];
for (i=0;i<numberofSnowflakes;i++)
{
sf[i] = new snowflake();
snowflakes = i;
}
Okay so I have seen a few answers but the asker wants the snow to be exclusively piling up on the text. Exact piling (according to the snow in background) would be really very difficult but I would like to suggest a (maybe complex) working method.
Make images.
A lots of them. With the text 'I love You' and contents of snow different in each. This maybe similar to creating picture frames from a video.
Define a div. Change your picture every x seconds, where x depends on number of images you have made.
You can use the following code for changing image:
function change_background( new_image_source ) {
var myimage = $( '#myimage' );
myimage.attr( 'src', new_image_source );
setTimeout( function () {
change_background( 'new image source here' );
}, 100);
}
This code will change the image after 0.1. This can hopefully give you the required effect but you will need to make a lot of pictures that differ slightly. I would make lots of pictures for my gf (if I had one).

Categories

Resources