I just need an advice on where to look at or where to start as right now I am confused. Not sure if this is CSS issue, javascript, or fabric.js
Problem:
- I am adding a background image to canvas and sometimes it behaves really strange. And canvas is distorting itself sometimes on click, sometimes on resize, sometimes just without doing nothing. But this does not happen always.
These are two videos with the issue:
https://jumpshare.com/v/4FU3xWjqk4h6j0OwqaFs (this is just without
doing nothing).
https://jumpshare.com/v/tBS7NJC5VgccWnIssETw (this is me clicking on canvas)
This is how I add an image to canvas (fetching url via API):
$scope.openBlob = function($event, imageURL) {
$rootScope.isLoading();
$rootScope.centerSpinner();
if ($rootScope.currentTab === 1) {
var upload_thumb = angular.element($event.target.parentElement);
var circular = angular.element('<md-progress-circular md-mode="indeterminate" id="circular-images" md-diameter="30" ng-show="isAlsoLoading"></md-progress-circular>');
$compile(circular)($scope);
upload_thumb.prepend(circular);
} else {
var upload_thumb = angular.element($event.target.parentElement);
var circular = angular.element('<md-progress-circular md-mode="indeterminate" id="circular-new-middle" md-diameter="30" ng-show="isAlsoLoading"></md-progress-circular>');
$compile(circular)($scope);
upload_thumb.prepend(circular);
}
$rootScope.isAlsoLoading = true;
$http({
method: "GET",
url: imageURL.full,
responseType: "blob"
}).then(
function(res) {
var reader = new FileReader();
reader.readAsDataURL(res.data);
reader.onload = function(e) {
canvas.loadMainImage(e.target.result);
$rootScope.started = true;
$rootScope.uploaded = true;
$rootScope.changeStyling();
};
},
function(res) {
console.log("For some reasons it failed to open", res);
});
if ($rootScope.currentTab === 1) {
$rootScope.$on('editor.mainImage.loaded', function() {
$rootScope.isAlsoLoading = false;
upload_thumb.find("#circular-images").remove();
$rootScope.resetFilters();
$rootScope.isNotLoading();
});
} else {
$rootScope.$on('editor.mainImage.loaded', function() {
$rootScope.isAlsoLoading = false;
upload_thumb.find("#circular-new-middle").remove();
$rootScope.resetFilters();
$rootScope.isNotLoading();
});
}
}
LoadMainImage function content:
loadMainImage: function(url, height, width, dontFit, callback) {
var object;
fabric.util.loadImage(url, function(img) {
//img.crossOrigin = 'anonymous';
object = new fabric.Image(img, canvas.imageStatic);
object.name = 'mainImage';
if (width && height) {
object.width = width;
object.height = height;
}
canvas.mainImage = object;
canvas.fabric.forEachObject(function(obj) {
console.log('image object', obj);
if (obj && obj.name == 'mainImage') {
canvas.fabric.remove(obj);
}
});
canvas.fabric.add(object);
object.top = -0.5;
object.left = -0.5;
object.moveTo(0);
canvas.fabric.setHeight(object.height);
canvas.fabric.setWidth(object.width);
canvas.original.height = object.height;
canvas.original.width = object.width;
$rootScope.canvaswidth = canvas.original.width;
$rootScope.canvasheight = canvas.original.height;
$rootScope.calculateImageType(canvas.original.width, canvas.original.height);
if ($rootScope.gridadded) {
$rootScope.removeGrid();
$rootScope.addGrid();
}
if (!dontFit) {
canvas.fitToScreen();
}
$rootScope.$apply(function() {
$rootScope.$emit('editor.mainImage.loaded');
});
if (callback) {
callback();
}
});
},
Fit to screen function:
$scope.fitToScreen = function () {
$scope.zoom = canvas.currentZoom * 100;
if (canvas.mainImage) {
var oWidth = canvas.mainImage.width,
oHeight = canvas.mainImage.height;
} else {
var oWidth = canvas.original.width,
oHeight = canvas.original.height;
}
var maxScale = Math.min(3582 / oHeight, 5731 / oWidth) * 100,
minScale = Math.min(141 / oHeight, 211 / oWidth) * 100;
$scope.maxScale = Math.ceil(maxScale);
$scope.minScale = Math.ceil(minScale);
canvas.zoom(canvas.currentZoom);
};
UPDATE: I am assuming that it might be scaling (zooming in and zooming out issue)
Related
I want to visualize audio that is coming in from webrtc(JSSIP). I used WebAudio, connected analyzer node and it seems to be working fine with Firefox.
In chrome browser, thing seems to be not working. I can listen to the audio in Chrome but am not able to visualize it, it doesn't throw any error. I checked the array, it shows -Infinity.
What could be the reason for it? How can I visualize in chrome?
console.log(dataArray);
>> Float32Array(512) [-Infinity, -Infinity, -Infinity, -Infinity, -Infinity, -Infinity, -Infinity, …]
Code for the same
const AudioContext = window.AudioContext || window.webkitAudioContext;
var audioElement;
var audioCtx;
var analyser;
const canvas = document.getElementById("visualizer");
const canvasCtx = canvas.getContext("2d");
canvas.width = window.innerWidth - 50;
canvas.height = canvas.width / 2;
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var drawVisual;
const socketUrl = "wss://website.com:8089/ws";
var ua;
var configuration = {
sockets: [],
'uri': "",
'password': "",
'username': "",
'register': true
};
var room = "";
var session;
var callOptions = {
'mediaConstraints': { 'audio': true, 'video': false },
'pcConfig': {
'iceServers': [
{ 'urls': ['stun:stun.l.google.com:19302'] },
]
}
};
const callBtn = document.getElementById('call-btn');
const hangupBtn = document.getElementById('hangup-btn');
hangupBtn.setAttribute('disabled','disabled');
var initAudioElement = function () {
audioElement = new window.Audio();
audioElement.autoplay = true;
audioElement.crossOrigin = "anonymous";
document.body.appendChild(audioElement);
}
var audioCtxInit = function () {
try {
audioCtx = new AudioContext();
analyser = audioCtx.createAnalyser();
} catch (e) {
alert('Web audio not supported.');
}
var audioTrack = audioCtx.createMediaElementSource(audioElement);
audioTrack.connect(analyser);
analyser.connect(audioCtx.destination);
}
var initializeJsSIP = function () {
if (ua) {
ua.stop();
ua = null;
}
try {
const socket = new JsSIP.WebSocketInterface(socketUrl);
JsSIP.debug.disable('JsSIP:*'); // Debugger
configuration.sockets = [socket];
configuration.uri = "";
ua = new JsSIP.UA(configuration);
} catch (error) {
console.log(error);
}
ua.on("connecting", () => {
console.log('UA "connecting" event');
});
ua.on("connected", () => {
console.log('UA "connected" event');
});
ua.on("disconnected", () => {
console.log('UA "disconnected" event');
});
ua.on("registered", (data) => {
console.log('UA "registered" event', data);
});
ua.on("unregistered", () => {
console.log('UA "unregistered" event');
});
ua.on("registrationFailed", (data) => {
console.log('UA "registrationFailed" event', data);
});
ua.on("newRTCSession", ({ originator, session: rtcSession, request: rtcRequest }) => {
console.log('UA "newRTCSession" event');
// identify call direction
if (originator === "local") {
const foundUri = rtcRequest.to.toString();
const delimiterPosition = foundUri.indexOf(";") || null;
console.log(`foundUri: ${foundUri}`);
} else if (originator === "remote") {
const foundUri = rtcRequest.from.toString();
const delimiterPosition = foundUri.indexOf(";") || null;
console.log(`foundUri: ${foundUri}`);
}
if (session) { // hangup any existing call
session.terminate();
}
session = rtcSession;
session.on("failed", (data) => {
console.log('rtcSession "failed" event', data);
session = null;
});
session.on("ended", () => {
console.log('rtcSession "ended" event');
session = null;
});
session.on("accepted", () => {
console.log('rtcSession "accepted" event');
[
audioElement.srcObject,
] = session.connection.getRemoteStreams();
var playPromise = audioElement.play();
if (typeof playPromise !== "undefined") {
playPromise
.then(() => {
console.log("playing");
visualize();
})
.catch((error) => {
console.log(error);
});
}
});
});
ua.start();
}
function visualize() {
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
analyser.fftSize = 2 ** 10;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Float32Array(bufferLength);
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
function draw() {
drawVisual = requestAnimationFrame(draw);
analyser.getFloatFrequencyData(dataArray);
canvasCtx.fillStyle = 'rgb(0, 0, 0)';
canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
var barWidth = (WIDTH / bufferLength) * 2.5;
var barHeight;
var x = 0;
for (var i = 0; i < bufferLength; i++) {
barHeight = (dataArray[i] + 140) * 2;
canvasCtx.fillStyle = 'rgb(' + Math.floor(barHeight + 100) + ',50,50)';
canvasCtx.fillRect(x, HEIGHT - barHeight / 2, barWidth, barHeight / 2);
x += barWidth + 1;
}
}
draw();
}
callBtn.addEventListener('click', function () {
callBtn.setAttribute('disabled','disabled');
hangupBtn.removeAttribute('disabled');
if (!audioCtx) {
audioCtxInit();
}
// check if context is in suspended state (autoplay policy)
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
ua.call(room, callOptions);
});
hangupBtn.addEventListener('click', function () {
callBtn.removeAttribute('disabled');
hangupBtn.setAttribute('disabled', 'disabled');
if (drawVisual) {
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
canvasCtx.fillStyle = 'rgb(0, 0, 0)';
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
window.cancelAnimationFrame(drawVisual);
drawVisual = null;
}
ua.terminateSessions();
});
window.onload = function () {
readyStateTimer = setInterval(function () {
if (document.readyState === "complete") {
clearInterval(readyStateTimer);
initAudioElement();
initializeJsSIP();
}
}, 500);
}
window.onbeforeunload = function () {
audioElement.parentNode.removeChild(this.audioElement);
delete audioElement;
if (ua) {
ua.stop();
ua = null;
}
};
I'm trying to manage to apply an generated image of an audio graph for every div that a music url is found with a Google Chrome Extension.
However, the process of downloading the music from the url and processing the image, takes enough time that all of the images keep applying to the last div.
I'm trying to apply the images to each div as throughout the JQuery's each request. All the div's have the /renderload.gif gif playing, but only the last div flashes as the images finished processing one by one.
Example being that the src is being set to /renderload.gif for all 1,2,3,4,5
but once the sound blob was downloaded and image was generated, only 4-5 gets the images and it continues on loading the queue, repeating the issue.
Here's an example of what I'm trying to deal with.
Here's my latest attempts to add queueing to avoid lag by loading all the audios at once, but it seems the issue still persists.
// context.js
function Queue(){
var queue = [];
var offset = 0;
this.getLength = function(){
return (queue.length - offset);
}
this.isEmpty = function(){
return (queue.length == 0);
}
this.setEmpty = function(){
queue = [];
return true;
}
this.enqueue = function(item){
queue.push(item);
}
this.dequeue = function(){
if (queue.length == 0) return undefined;
var item = queue[offset];
if (++ offset * 2 >= queue.length){
queue = queue.slice(offset);
offset = 0;
}
return item;
}
this.peek = function(){
return (queue.length > 0 ? queue[offset] : undefined);
}
}
var audioqueue=new Queue();
var init=0;
var current=0;
var finished=0;
function RunGraphs(x) {
if (x==init) {
if (audioqueue.isEmpty()==false) {
current++;
var das=audioqueue.dequeue();
var divparent=das.find(".original-image");
var songurl=das.find(".Mpcs").find('span').attr("data-url");
console.log("is song url "+songurl);
console.log("is data here "+divparent.attr("title"));
divparent.css('width','110px');
divparent.attr('src','https://i.pinimg.com/originals/a4/f2/cb/a4f2cb80ff2ae2772e80bf30e9d78d4c.gif');
var blob = null;
var xhr = new XMLHttpRequest();
xhr.open("GET",songurl,true);
xhr.responseType = "blob";//force the HTTP response, response-type header to be blob
xhr.onload = function() {
blob = xhr.response;//xhr.response is now a blob object
console.log(blob);
SCWFRobloxAudioTool.generate(blob, {
canvas_width: 110,
canvas_height: 110,
bar_width: 1,
bar_gap : .2,
wave_color: "#ecb440",
download: false,
onComplete: function(png, pixels) {
if (init == x) {
divparent.attr('src',png);
finished++;
}
}
});
}
xhr.send();
OnHold(x);
}
}
}
function OnHold(x) {
if (x==init) {
if (current > finished+7) {
setTimeout(function(){
OnHold(x)
},150)
} else {
RunGraphs(x)
}
}
}
if (window.location.href.includes("/lib?Ct=DevOnly")){
functionlist=[];
current=0;
finished=0;
init++;
audioqueue.setEmpty();
$(".CATinner").each(function(index) {
(function(x){
audioqueue.enqueue(x);
}($(this)));
});
RunGraphs(init);
};
The SCWFAudioTool is from this github repository.
Soundcloud Waveform Generator
The Queue.js from a search request, slightly modified to have setEmpty support.Queue.js
Please read the edit part of the post
I mad a usable minimal example of your code in order to check your Queue and defer method. there seems to be no error that i can find (i don't have the html file and cant check for missing files. Please do that yourself by adding the if (this.status >= 200 && this.status < 400) check to the onload callback):
// context.js
function Queue(){
var queue = [];
var offset = 0;
this.getLength = function(){
return (queue.length - offset);
}
this.isEmpty = function(){
return (queue.length == 0);
}
this.setEmpty = function(){
queue = [];
return true;
}
this.enqueue = function(item){
queue.push(item);
}
this.dequeue = function(){
if (queue.length == 0) return undefined;
var item = queue[offset];
if (++ offset * 2 >= queue.length){
queue = queue.slice(offset);
offset = 0;
}
return item;
}
this.peek = function(){
return (queue.length > 0 ? queue[offset] : undefined);
}
}
var audioqueue=new Queue();
var init=0;
var current=0;
var finished=0;
function RunGraphs(x) {
if (x==init) {
if (audioqueue.isEmpty()==false) {
current++;
var songurl = audioqueue.dequeue();
console.log("is song url "+songurl);
var blob = null;
var xhr = new XMLHttpRequest();
xhr.open("GET",songurl,true);
xhr.responseType = "blob";//force the HTTP response, response-type header to be blob
xhr.onload = function() {
if (this.status >= 200 && this.status < 400) {
blob = xhr.response;//xhr.response is now a blob object
console.log('OK');
finished++;
} else {
console.log('FAIL');
}
}
xhr.send();
OnHold(x);
}
}
}
function OnHold(x) {
if (x==init) {
if (current > finished+7) {
setTimeout(function(){
OnHold(x)
},150)
} else {
RunGraphs(x)
}
}
}
var demoObject = new Blob(["0".repeat(1024*1024*2)]); // 2MB Blob
var demoObjectURL = URL.createObjectURL(demoObject);
if (true){
functionlist=[];
current=0;
finished=0;
init++;
audioqueue.setEmpty();
for(var i = 0; i < 20; i++)
audioqueue.enqueue(demoObjectURL);
RunGraphs(init);
};
Therefore if there are no errors left concerning missing files the only errors i can think off is related to the SCWFRobloxAudioTool.generate method.
Please check if the callback gets triggered correctly and that no errors accrue during conversion.
If you provide additional additional info, data or code i can look into this problem.
EDIT:
I looked into the 'SoundCloudWaveform' program and i think i see the problem:
The module is not made to handle multiple queries at once (there is only one global setting object. So every attempt to add another query to the api will override the callback of the previous one, and since the fileReader is a async call only the latest added callback will be executed.)
Please consider using an oop attempt of this api:
window.AudioContext = window.AudioContext || window.webkitAudioContext;
Array.prototype.max = function() {
return Math.max.apply(null, this);
};
function SoundCloudWaveform (){
this.settings = {
canvas_width: 453,
canvas_height: 66,
bar_width: 3,
bar_gap : 0.2,
wave_color: "#666",
download: false,
onComplete: function(png, pixels) {}
}
this.generate = function(file, options) {
// preparing canvas
this.settings.canvas = document.createElement('canvas');
this.settings.context = this.settings.canvas.getContext('2d');
this.settings.canvas.width = (options.canvas_width !== undefined) ? parseInt(options.canvas_width) : this.settings.canvas_width;
this.settings.canvas.height = (options.canvas_height !== undefined) ? parseInt(options.canvas_height) : this.settings.canvas_height;
// setting fill color
this.settings.wave_color = (options.wave_color !== undefined) ? options.wave_color : this.settings.wave_color;
// setting bars width and gap
this.settings.bar_width = (options.bar_width !== undefined) ? parseInt(options.bar_width) : this.settings.bar_width;
this.settings.bar_gap = (options.bar_gap !== undefined) ? parseFloat(options.bar_gap) : this.settings.bar_gap;
this.settings.download = (options.download !== undefined) ? options.download : this.settings.download;
this.settings.onComplete = (options.onComplete !== undefined) ? options.onComplete : this.settings.onComplete;
// read file buffer
var reader = new FileReader();
var _this = this;
reader.onload = function(event) {
var audioContext = new AudioContext()
audioContext.decodeAudioData(event.target.result, function(buffer) {
audioContext.close();
_this.extractBuffer(buffer);
});
};
reader.readAsArrayBuffer(file);
}
this.extractBuffer = function(buffer) {
buffer = buffer.getChannelData(0);
var sections = this.settings.canvas.width;
var len = Math.floor(buffer.length / sections);
var maxHeight = this.settings.canvas.height;
var vals = [];
for (var i = 0; i < sections; i += this.settings.bar_width) {
vals.push(this.bufferMeasure(i * len, len, buffer) * 10000);
}
for (var j = 0; j < sections; j += this.settings.bar_width) {
var scale = maxHeight / vals.max();
var val = this.bufferMeasure(j * len, len, buffer) * 10000;
val *= scale;
val += 1;
this.drawBar(j, val);
}
if (this.settings.download) {
this.generateImage();
}
this.settings.onComplete(this.settings.canvas.toDataURL('image/png'), this.settings.context.getImageData(0, 0, this.settings.canvas.width, this.settings.canvas.height));
// clear canvas for redrawing
this.settings.context.clearRect(0, 0, this.settings.canvas.width, this.settings.canvas.height);
},
this.bufferMeasure = function(position, length, data) {
var sum = 0.0;
for (var i = position; i <= (position + length) - 1; i++) {
sum += Math.pow(data[i], 2);
}
return Math.sqrt(sum / data.length);
},
this.drawBar = function(i, h) {
this.settings.context.fillStyle = this.settings.wave_color;
var w = this.settings.bar_width;
if (this.settings.bar_gap !== 0) {
w *= Math.abs(1 - this.settings.bar_gap);
}
var x = i + (w / 2),
y = this.settings.canvas.height - h;
this.settings.context.fillRect(x, y, w, h);
},
this.generateImage = function() {
var image = this.settings.canvas.toDataURL('image/png');
var link = document.createElement('a');
link.href = image;
link.setAttribute('download', '');
link.click();
}
}
console.log(new SoundCloudWaveform());
Also consider simply using an array for the queue:
function Queue(){
var queue = [];
var offset = 0;
this.getLength = function(){
return (queue.length - offset);
}
this.isEmpty = function(){
return (queue.length == 0);
}
this.setEmpty = function(){
queue = [];
return true;
}
this.enqueue = function(item){
queue.push(item);
}
this.dequeue = function(){
if (queue.length == 0) return undefined;
var item = queue[offset];
if (++ offset * 2 >= queue.length){
queue = queue.slice(offset);
offset = 0;
}
return item;
}
this.peek = function(){
return (queue.length > 0 ? queue[offset] : undefined);
}
}
var q = new Queue();
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
console.log(q.dequeue());
console.log(q.dequeue());
console.log(q.dequeue());
console.log(q.dequeue());
var q2 = [];
q2.push(1)
q2.push(2)
q2.push(3)
console.log(q2.shift());
console.log(q2.shift());
console.log(q2.shift());
console.log(q2.shift());
It prevents confusion ant the speedup of it is minimal in your application.
On your open method on the xhr object, set the parameter to true, also... try using the onload() instead of onloadend(). Good Luck!
var xmlhttp = new XMLHttpRequest(),
method = 'GET',
url = 'https://developer.mozilla.org/';
xmlhttp.open(method, url, true);
xmlhttp.onload = function () {
// Do something with the retrieved data
};
xmlhttp.send();
Honestly do not know whats happen, this was working this morning, have not changed a thing but now when i click my button to generate my images I get this error.
Can anyone tell me why and how to fix this please.
Error
test initMock test generate 1
TypeError: Cannot create property 'style' on string 'a'
at i._createCanvasElement (fabric.min.js:2)
at i._createLowerCanvas (fabric.min.js:2)
at i._initStatic (fabric.min.js:2)
at initialize (fabric.min.js:3)
at new i (fabric.min.js:1)
at b.$scope.uploadImage (controller.js:855)
at b.$scope.generate (controller.js:929)
at fn (eval at compile (angular.js:15500), <anonymous>:4:144)
at e (angular.js:27285)
at b.$eval (angular.js:18372)
my functions
$scope.uploadImage = function (where) {
var deferred = $q.defer();
if (where === 'upload') {
var f = document.getElementById('uploadCreative').files[0];
var r = new FileReader();
r.onload = function () {
var image = new Image();
image.src = r.result;
image.onload = function () {
$scope.resize(image.src).then(function (response) {
deferred.resolve(response);
console.log('hehe NO!');
console.log('hehe NO!');
}).catch(function (response) {
})
}
};
r.readAsDataURL(f);
}
if (where === 'local') {
function ax(a, callback) {
callback(localStorage.getItem(a));
}
var loadCanvas = new fabric.Canvas('a');
divHeight = $('.image-builder').height();
if ($scope.format === '1') {
Aratio = 0.67;
} else if ($scope.format === '2') {
Aratio = 0.56;
} else if ($scope.format === '3') {
divHeight = divHeight / 1.5;
Aratio = 2;
} else if ($scope.format === '4') {
Aratio = 0.67;
} else {
Aratio = 1
}
loadCanvas.setHeight(divHeight - 15);
loadCanvas.setWidth((divHeight - 15) * Aratio);
if (localStorage.getItem('backgroundImage') !== 'null') {
background = localStorage.getItem('backgroundImage');
var imgBc = new Image();
imgBc.onload = function () {
// this is syncronous
Iwidth = this.width;
Iheight = this.height;
var f_img = new fabric.Image(imgBc);
loadCanvas.setBackgroundImage(f_img, loadCanvas.renderAll.bind(loadCanvas), {
scaleY: loadCanvas.height / Iheight,
scaleX: loadCanvas.width / Iwidth
});
var test = ax('CC-canvas', function (response) {
loadCanvas.loadFromJSON(response, function () {
loadCanvas.renderAll();
$scope.resize(loadCanvas.toDataURL()).then(function (response) {
deferred.resolve(response);
}).catch(function (response) {
})
});
});
};
imgBc.src = background;
} else {
var test = ax('CC-canvas', function (response) {
loadCanvas.loadFromJSON(response, function () {
loadCanvas.renderAll();
$scope.resize(loadCanvas.toDataURL()).then(function (response) {
deferred.resolve(response);
}).catch(function (response) {
console.log(response);
})
});
});
}
}
return deferred.promise;
};
$scope.generate = function () {
$scope.generating = true;
$scope.generateBtn = 'Generating';
for (i = 0; i < $scope.gallery.length; i++) {
$scope.select[i] = '';
}
$scope.gallery = [];
$scope.checkPhoto = [];
console.log("test generate 1");
$scope.uploadImage($scope.creative).then(function (result) {
console.log("test generate 2");
$scope.generateCanvas(result, $scope.left, $scope.tops, $scope.wi, $scope.hi, $scope.per, $scope.btmR, $scope.btmL, $scope.backUrl)
.then(function () {
$timeout(function () {
//push final photo to array befor send to back end
$scope.photosToPhp.push(canvas2.toDataURL());
}, 800);
if ($scope.count < ($scope.photos[$scope.format].length - 1)) {
$scope.generate();
$scope.count++;
$scope.left = $scope.photos[$scope.format][$scope.count]['left'];
$scope.tops = $scope.photos[$scope.format][$scope.count]['tops'];
$scope.wi = $scope.photos[$scope.format][$scope.count]['wi'];
$scope.hi = $scope.photos[$scope.format][$scope.count]['hi'];
$scope.per = $scope.photos[$scope.format][$scope.count]['per'];
$scope.btmR = $scope.photos[$scope.format][$scope.count]['btmR'];
$scope.btmL = $scope.photos[$scope.format][$scope.count]['btmL'];
$scope.backUrl = "/mm3/public/img/formats/" + $scope.format + "/" + $scope.photos[$scope.format][$scope.count]['backUrl'];
$scope.$apply();
console.log("test generate 3");
} else {
//all photos've been pushed now sending it to back end
$timeout(function () {
// console.log($scope.photosToPhp[0]);
$http.post('/mm3/savePhoto', $scope.photosToPhp).then(function (success) {
$scope.generating = false;
$scope.generateBtn = 'Generate';
//creating mock up gallery
for (var x = 0; x < success.data.photos; x++) {
var file = '/mm3/tmp/' + success.data.folder + "/out" + x + ".png";
$scope.gallery.push(file);
}
$scope.photosToPhp = [];
}, function (error) {
});
}, 800);
$scope.count = 0;
$scope.left = $scope.photos[$scope.format][$scope.count]['left'];
$scope.tops = $scope.photos[$scope.format][$scope.count]['tops'];
$scope.wi = $scope.photos[$scope.format][$scope.count]['wi'];
$scope.hi = $scope.photos[$scope.format][$scope.count]['hi'];
$scope.per = $scope.photos[$scope.format][$scope.count]['per'];
$scope.btmR = $scope.photos[$scope.format][$scope.count]['btmR'];
$scope.btmL = $scope.photos[$scope.format][$scope.count]['btmL'];
$scope.backUrl = "/mm3/public/img/formats/" + $scope.format + "/" + $scope.photos[$scope.format][$scope.count]['backUrl'];
$scope.$apply();
}
console.log("test generate 4");
})
.catch(function (errorUrl) {
console.log(errorUrl);
})
})
};
Solved bu downgrading fabric js to 1.5 not 1.7 that i upgraded to, dont know why this worked but it dose
I would like to be able to select a music folder and shuffle all of the songs once on load and Have a skip button + return the song file name to the visualizer so that It can visualize each song. I'm still learning array's and for loops so I'm unsure of how to go about this. I also want to keep away from extra libraries for now because everything is already provided. Heres a code snippet of what I have so far
window.onload = function() {
var file = document.getElementById("file");
var audio = document.getElementById("audio");
file.onchange = function() {
var files = this.files;
audio.src = URL.createObjectURL(files[0]);
audio.load();
audio.play();
var context = new AudioContext();
var src = context.createMediaElementSource(audio);
var analyser = context.createAnalyser();
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
src.connect(analyser);
analyser.connect(context.destination);
analyser.fftSize = 256;
var bufferLength = analyser.frequencyBinCount;
console.log(bufferLength);
var dataArray = new Uint8Array(bufferLength);
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var barWidth = (WIDTH / bufferLength) * 1;
var barHeight;
var x = 0;
function renderFrame() {
requestAnimationFrame(renderFrame);
x = 0;
analyser.getByteFrequencyData(dataArray);
ctx.fillStyle = "#1b1b1b";
ctx.fillRect(0, 0, WIDTH, HEIGHT);
for (var i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
var r = 5;
var g = 195;
var b = 45;
ctx.fillStyle = "rgb(5,195,45)"
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
x += barWidth + 2;
}
}
audio.play();
renderFrame();
};
};
#file {
position: fixed;
top: 10px;
left: 10px;
z-index: 100;
}
#canvas {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
audio {
position: fixed;
left: 350px;
top: 10px;
width: calc(50% - 20px);
}
<div id="content">
<input type="file" id="file" accept="audio/*" />
<canvas id="canvas"></canvas>
<audio id="audio" controls></audio>
</div>
You can set webkitdirectory and allowdirs attributes at <input type="file"> element to enable directory upload.
Recursively, or using Promise repeatedly iterate directories, including directories within directories, push all files in directories to a globally defined array of File objects, see How to upload and list directories at firefox and chrome/chromium using change and drop events; where JavaScript at Answer is modified to remove drop event handler, which is specifically not a part of requirement.
Use Array.prototype.reduce() and Promise to call a function for each File object in sequence to play the media and return a fulfilled Promise at ended event of HTMLMediaElement; for example, the anonymous function at change handler at Question, modified where necessary to achieve expected requirement of a playlist created from upload of N directories or N nested directories. Note that when calling AudioContent.createMediaElementSource() more than once with the same <audio> element as parameter an exception will be thrown
Uncaught DOMException: Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode
see remove createMediaElementSource. You can define the variables globally and reference the variable using OR || to avoid the exception.
The created Blob URL is assigned to a variable and revoked at ended event of HTMLMediaElement and before being re-assigned when function is called again, if ended event is not reached.
Included <select> element for ability to select and play any one of the uploaded files.
var input = document.getElementById("file");
var audio = document.getElementById("audio");
var selectLabel = document.querySelector("label[for=select]");
var audioLabel = document.querySelector("label[for=audio]");
var select = document.querySelector("select");
var context = void 0,
src = void 0,
res = [],
url = "";
function processDirectoryUpload(event) {
var webkitResult = [];
var mozResult = [];
var files;
console.log(event);
select.innerHTML = "";
// do mozilla stuff
function mozReadDirectories(entries, path) {
console.log("dir", entries, path);
return [].reduce.call(entries, function(promise, entry) {
return promise.then(function() {
return Promise.resolve(entry.getFilesAndDirectories() || entry)
.then(function(dir) {
return dir
})
})
}, Promise.resolve())
.then(function(items) {
var dir = items.filter(function(folder) {
return folder instanceof Directory
});
var files = items.filter(function(file) {
return file instanceof File
});
if (files.length) {
// console.log("files:", files, path);
mozResult = mozResult.concat.apply(mozResult, files);
}
if (dir.length) {
// console.log(dir, dir[0] instanceof Directory);
return mozReadDirectories(dir, dir[0].path || path);
} else {
if (!dir.length) {
return Promise.resolve(mozResult).then(function(complete) {
return complete
})
}
}
})
};
function handleEntries(entry) {
let file = "webkitGetAsEntry" in entry ? entry.webkitGetAsEntry() : entry
return Promise.resolve(file);
}
function handleFile(entry) {
return new Promise(function(resolve) {
if (entry.isFile) {
entry.file(function(file) {
listFile(file, entry.fullPath).then(resolve)
})
} else if (entry.isDirectory) {
var reader = entry.createReader();
reader.readEntries(webkitReadDirectories.bind(null, entry, handleFile, resolve))
} else {
var entries = [entry];
return entries.reduce(function(promise, file) {
return promise.then(function() {
return listDirectory(file)
})
}, Promise.resolve())
.then(function() {
return Promise.all(entries.map(function(file) {
return listFile(file)
})).then(resolve)
})
}
})
function webkitReadDirectories(entry, callback, resolve, entries) {
console.log(entries);
return listDirectory(entry).then(function(currentDirectory) {
console.log(`iterating ${currentDirectory.name} directory`, entry);
return entries.reduce(function(promise, directory) {
return promise.then(function() {
return callback(directory)
});
}, Promise.resolve())
}).then(resolve);
}
}
function listDirectory(entry) {
console.log(entry);
return Promise.resolve(entry);
}
function listFile(file, path) {
path = path || file.webkitRelativePath || "/" + file.name;
console.log(`reading ${file.name}, size: ${file.size}, path:${path}`);
webkitResult.push(file);
return Promise.resolve(webkitResult)
};
function processFiles(files) {
Promise.all([].map.call(files, function(file, index) {
return handleEntries(file, index).then(handleFile)
}))
.then(function() {
console.log("complete", webkitResult);
res = webkitResult;
res.reduce(function(promise, track) {
return promise.then(function() {
return playMusic(track)
})
}, displayFiles(res))
})
.catch(function(err) {
alert(err.message);
})
}
if ("getFilesAndDirectories" in event.target) {
return (event.type === "drop" ? event.dataTransfer : event.target).getFilesAndDirectories()
.then(function(dir) {
if (dir[0] instanceof Directory) {
console.log(dir)
return mozReadDirectories(dir, dir[0].path || path)
.then(function(complete) {
console.log("complete:", webkitResult);
event.target.value = null;
});
} else {
if (dir[0] instanceof File && dir[0].size > 0) {
return Promise.resolve(dir)
.then(function() {
console.log("complete:", mozResult);
res = mozResult;
res.reduce(function(promise, track) {
return promise.then(function() {
return playMusic(track)
})
}, displayFiles(res))
})
} else {
if (dir[0].size == 0) {
throw new Error("could not process '" + dir[0].name + "' directory" + " at drop event at firefox, upload folders at 'Choose folder...' input");
}
}
}
}).catch(function(err) {
alert(err)
})
}
files = event.target.files;
if (files) {
processFiles(files)
}
}
function displayFiles(files) {
select.innerHTML = "";
return Promise.all(files.map(function(file, index) {
return new Promise(function(resolve) {
var option = new Option(file.name, index);
select.appendChild(option);
resolve()
})
}))
}
function handleSelectedSong(event) {
if (res.length) {
var index = select.value;
var track = res[index];
playMusic(track)
.then(function(filename) {
console.log(filename + " playback completed")
})
} else {
console.log("No songs to play")
}
}
function playMusic(file) {
return new Promise(function(resolve) {
audio.pause();
audio.onended = function() {
audio.onended = null;
if (url) URL.revokeObjectURL(url);
resolve(file.name);
}
if (url) URL.revokeObjectURL(url);
url = URL.createObjectURL(file);
audio.load();
audio.src = url;
audio.play();
audioLabel.textContent = file.name;
context = context || new AudioContext();
src = src || context.createMediaElementSource(audio);
src.disconnect(context);
var analyser = context.createAnalyser();
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
src.connect(analyser);
analyser.connect(context.destination);
analyser.fftSize = 256;
var bufferLength = analyser.frequencyBinCount;
console.log(bufferLength);
var dataArray = new Uint8Array(bufferLength);
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var barWidth = (WIDTH / bufferLength) * 1;
var barHeight;
var x = 0;
function renderFrame() {
requestAnimationFrame(renderFrame);
x = 0;
analyser.getByteFrequencyData(dataArray);
ctx.fillStyle = "#1b1b1b";
ctx.fillRect(0, 0, WIDTH, HEIGHT);
for (var i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
var r = 5;
var g = 195;
var b = 45;
ctx.fillStyle = "rgb(5,195,45)"
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
x += barWidth + 2;
}
}
renderFrame();
})
}
input.addEventListener("change", processDirectoryUpload);
select.addEventListener("change", handleSelectedSong);
<div id="content">
Upload directory: <input id="file" type="file" accept="audio/*" directory allowdirs webkitdirectory/><br>
<br>Now playing: <label for="audio"></label><br>
<br><label for="select">Select a song to play:</label><br>
<select id="select">
</select>
<canvas id="canvas"></canvas>
<audio id="audio" controls></audio>
</div>
How can I use PIXI js to animate from spritesheets using TiledSprites? I need to animate a tiled sprite background from a sprite sheet.
Currently there is API calls to animate a tiled sprite in the PIXI.js API. I have created the following class to help me load and animate tiled backgrounds.
/////////////////////////////////////////////////////////////////////////////
/// Tiling Sprite Animation
/////////////////////////////////////////////////////////////////////////////
(function() {
PIXI.TilingSpriteAnimation = function(texture, frames, rows, frametime, loop) {
PIXI.TilingSprite.call(
this, texture,
VIEWPORTWIDTH,
this._texture.baseTexture.height);
this._stop = true;
this._texture = new PIXI.Texture(texture);
this.frameTime = frametime;
this.loop = loop || true;
this.curX = 0;
this.curY = 0;
this.fh = this._texture.height / rows;
this.fw = this._texture.width / frames;
this.ticks = 0;
this.maxFrames = frames;
this.maxRows = rows;
this.done = false;
this.calculateFrame();
};
PIXI.TilingSpriteAnimation.prototype = Object.create( PIXI.TilingSprite.prototype );
PIXI.TilingSpriteAnimation.prototype.constructor = PIXI.TilingSpriteAnimation;
Object.defineProperty(PIXI.TilingSpriteAnimation.prototype, 'texture', {
get: function() {
return this._texture;
}
});
PIXI.TilingSpriteAnimation.prototype.update = function() {
console.log(this.ticks);
if(!this._stop) {
this.ticks += 1;
}
if (this.done == false) {
if (this.ticks >= this.frameTime) {
this.curX++;
this.ticks = 0;
if (this.curX == this.maxFrames) {
this.curX = 0;
this.curY++;
if (this.curY == this.maxRows) {
this.curY = 0;
if (!this.loop)
this.done = true;
}
}
this.calculateFrame();
}
}
};
PIXI.TilingSpriteAnimation.prototype.goto = function(frame, row) {
this.curX = frame;
this.curY = row || 0;
};
PIXI.TilingSpriteAnimation.prototype.stop = function() {
this._stop = true;
};
PIXI.TilingSpriteAnimation.prototype.play = function() {
this._stop = false;
};
PIXI.TilingSpriteAnimation.prototype.calculateFrame = function() {
this.texture.frame.x = this.curX * this.fw;
this.texture.frame.y = this.curY * this.fh;
this.texture.frame.width = this.fw;
this.texture.frame.height = this.fh;
this.texture.setFrame(this.texture.frame);
this.generateTilingTexture(this.texture);
};
}).call(this);
This code is however highly inefficient because it calculates a new TiledTexture each time a new frame is entered. How can I optimize this?
I struggeled some time with this but came up with the following. I hope it helps
/////////////////////////////////////////////////////////////////////////////
/// Tiling Sprite Animation
/////////////////////////////////////////////////////////////////////////////
(function() {
PIXI.TilingSpriteAnimation = function(texture, frames, frametime, loop) {
PIXI.TilingSprite.call(
this, texture,
VIEWPORTWIDTH,
VIEWPORTHEIGHT);
this._stop = true;
this._texture = new PIXI.Texture(texture);
this.frameTime = frametime;
this.loop = loop || true;
this.curX = 0;
this.fh = this._texture.height;
this.fw = this._texture.width / frames;
this.ticks = 0;
this.maxFrames = frames;
for (var i=0;i<frames;i++){
this.preLoadFrame(i);
}
this.calculateFrame();
};
PIXI.TilingSpriteAnimation.prototype = Object.create( PIXI.TilingSprite.prototype );
PIXI.TilingSpriteAnimation.prototype.constructor = PIXI.TilingSpriteAnimation;
Object.defineProperty(PIXI.TilingSpriteAnimation.prototype, 'texture', {
get: function() {
return this._texture;
}
});
PIXI.TilingSpriteAnimation.prototype.update = function() {
if (this._stop == false) {
this.ticks += 1;
if (this.ticks >= this.frameTime) {
this.curX++;
this.ticks = 0;
if (this.curX == this.maxFrames) {
this.curX = 0;
if (!this.loop) {
this._stop = true;
}
}
this.calculateFrame();
}
}
};
PIXI.TilingSpriteAnimation.prototype.goto = function(frame) {
this.curX = frame;
};
PIXI.TilingSpriteAnimation.prototype.stop = function() {
this._stop = true;
};
PIXI.TilingSpriteAnimation.prototype.play = function() {
this._stop = false;
};
PIXI.TilingSpriteAnimation.prototype.calculateFrame = function() {
this.tilingTexture = PIXI.Texture.fromFrame("texture" + this.curX);
};
PIXI.TilingSpriteAnimation.prototype.preLoadFrame = function(frame) {
var text = new PIXI.TilingSprite(this.texture);
text.texture.frame.x = frame * this.fw;
text.texture.frame.y = 0;
text.texture.frame.width = this.fw;
text.texture.frame.height = this.fh;
text.texture.setFrame(text.texture.frame);
text.generateTilingTexture(text);
PIXI.Texture.addTextureToCache(text.tilingTexture, "texture" + frame)
};
}).call(this);