Related
I'm working on an audio analysis project and I was looking for a way to get the bpm from an mp3 / wav audio using JavaScript.
This is the only post about it that I found: Detecting bpm of audio input using javascript?
This is the only solution I found:
(function() {
var AUDIO_URL = 'media/song.mp3';
var CHANNELS, NUMBER_OF_PREVIOUS_SAMPLES, SAMPLES_PER_INSTANT_ENERGY, SAMPLE_RATE, THRESHOLD_CONSTANT, VARIANCE_COEFFICIENT, audioContext;
var audioSample, beatDetector, beatDetectorVisualisation, beatVisualisation, getAudioContext, getOfflineAudioContext, loadAudioFromUrl, main;
var playTrack, sampleLengthSeconds, timeline, trackStartTime, updateAudioFromArrayBuffer, updateAudioFromPcmData, updateBeats, updateSongPlace;
var updateVisualisation, windowEnd, windowStart,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) child[key] = parent[key];
} function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype; return child;
},
__hasProp = {}.hasOwnProperty;
THRESHOLD_CONSTANT = 1.5;
VARIANCE_COEFFICIENT = 0;
SAMPLES_PER_INSTANT_ENERGY = 1024;
NUMBER_OF_PREVIOUS_SAMPLES = 43;
CHANNELS = 1;
SAMPLE_RATE = 44100;
audioContext = null;
audioSample = null;
sampleLengthSeconds = 0;
trackStartTime = 0;
beatVisualisation = null;
beatDetectorVisualisation = null;
beatDetector = null;
windowStart = 0;
windowEnd = null;
timeline = null;
getAudioContext = function() {
var AudioContext;
AudioContext = window.AudioContext || window.webkitAudioContext;
if (audioContext == null) {
audioContext = new AudioContext();
}
return audioContext;
};
getOfflineAudioContext = function(channels, length, sampleRate) {
var OfflineAudioContext;
OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;
return new OfflineAudioContext(channels, length, sampleRate);
};
this.SoundEnergyBeatDetector = (function() {
var BEAT_MIN_DISTANCE_SAMPLES, IMPULSE_TRAIN_SIZE, MAX_DISTANCE_MULTIPLIER, MAX_SEARCH_WINDOW_SIZE;
SAMPLE_RATE = 44100;
BEAT_MIN_DISTANCE_SAMPLES = 10;
MAX_DISTANCE_MULTIPLIER = 2;
IMPULSE_TRAIN_SIZE = 108;
MAX_SEARCH_WINDOW_SIZE = 2;
function SoundEnergyBeatDetector() {}
SoundEnergyBeatDetector.prototype.detectBeats = function(
pcmAudioData, previousEnergyVarianceCoefficient, previousAverageEnergyCoefficient, samplesPerInstantEnergy, numberOfPreviousEnergies) {
var c, currentIndex, currentTimeSeconds, distanceBetweenBeatIndexes, distanceInEnergyIndexBetweenBeats, i, instantEnergySum, lastBeatIndex;
var maxCountIndex, meanCount, pcm, previousEnergies, previousEnergiesAverage, previousEnergiesIndex, previousEnergiesSum, previousEnergiesVariance;
var previousEnergy, sumOfDifferencesFromAverage, threshold, v, _i, _j, _k, _len, _len1, _len2, _ref;
this.maximumEnergies = [];
distanceInEnergyIndexBetweenBeats = [];
lastBeatIndex = 0;
this.energies = [];
this.averageEnergies = [];
this.maxEnergy = 0;
previousEnergies = [];
previousEnergiesIndex = 0;
instantEnergySum = 0;
for (i = _i = 0, _len = pcmAudioData.length; _i < _len; i = ++_i) {
pcm = pcmAudioData[i];
instantEnergySum += Math.pow(pcm, 2);
if (i % samplesPerInstantEnergy !== 0) {
continue;
}
if (instantEnergySum > this.maxEnergy) {
this.maxEnergy = instantEnergySum;
}
currentTimeSeconds = i / SAMPLE_RATE;
this.energies.push([currentTimeSeconds, instantEnergySum]);
if (previousEnergies.length < numberOfPreviousEnergies) {
previousEnergies.push(instantEnergySum);
} else {
previousEnergiesSum = 0;
for (_j = 0, _len1 = previousEnergies.length; _j < _len1; _j++) {
previousEnergy = previousEnergies[_j];
previousEnergiesSum += previousEnergy;
}
previousEnergiesAverage = previousEnergiesSum / numberOfPreviousEnergies;
sumOfDifferencesFromAverage = 0;
for (_k = 0, _len2 = previousEnergies.length; _k < _len2; _k++) {
previousEnergy = previousEnergies[_k];
sumOfDifferencesFromAverage += Math.pow(previousEnergy - previousEnergiesAverage, 2);
}
previousEnergiesVariance = sumOfDifferencesFromAverage / numberOfPreviousEnergies;
v = previousEnergiesVariance * previousEnergyVarianceCoefficient;
c = v + parseFloat(previousAverageEnergyCoefficient);
threshold = c * previousEnergiesAverage;
this.averageEnergies.push([currentTimeSeconds, threshold]);
if (instantEnergySum > threshold) {
currentIndex = this.averageEnergies.length - 1;
distanceBetweenBeatIndexes = currentIndex - lastBeatIndex;
if (distanceBetweenBeatIndexes > BEAT_MIN_DISTANCE_SAMPLES) {
lastBeatIndex = currentIndex;
this.maximumEnergies.push(currentTimeSeconds);
distanceInEnergyIndexBetweenBeats.push(distanceBetweenBeatIndexes);
}
}
previousEnergies.splice(previousEnergiesIndex, 1, instantEnergySum);
}
previousEnergiesIndex++;
if (previousEnergiesIndex >= numberOfPreviousEnergies) {
previousEnergiesIndex = 0;
}
instantEnergySum = 0;
}
_ref = this._calculateTempo(distanceInEnergyIndexBetweenBeats, numberOfPreviousEnergies, samplesPerInstantEnergy), meanCount = _ref[0], maxCountIndex = _ref[1];
return this._calculateConvolution(meanCount, maxCountIndex);
};
SoundEnergyBeatDetector.prototype._calculateTempo = function(distanceInEnergyIndexBetweenBeats, numberOfPreviousEnergies, samplesPerInstantEnergy) {
var a, b, beatDistanceCount, beatDistanceCounts, distance, divisor, i, maxCountIndex, maxCountSoFar, maxDistanceBetwenBeats, meanCount, neighbourCount, neighbourIndex, _i, _j, _k, _len, _len1;
maxDistanceBetwenBeats = numberOfPreviousEnergies * MAX_DISTANCE_MULTIPLIER;
beatDistanceCounts = [];
for (i = _i = 0; 0 <= maxDistanceBetwenBeats ? _i <= maxDistanceBetwenBeats : _i >= maxDistanceBetwenBeats; i = 0 <= maxDistanceBetwenBeats ? ++_i : --_i) {
beatDistanceCounts.push(0);
}
for (_j = 0, _len = distanceInEnergyIndexBetweenBeats.length; _j < _len; _j++) {
distance = distanceInEnergyIndexBetweenBeats[_j];
if (distance < maxDistanceBetwenBeats) {
beatDistanceCounts[distance]++;
}
}
maxCountIndex = 0;
maxCountSoFar = 0;
for (i = _k = 0, _len1 = beatDistanceCounts.length; _k < _len1; i = ++_k) {
beatDistanceCount = beatDistanceCounts[i];
if (beatDistanceCount > maxCountSoFar) {
maxCountSoFar = beatDistanceCount;
maxCountIndex = i;
}
}
if (maxCountIndex === beatDistanceCounts.length - 1) {
neighbourIndex = maxCountIndex - 1;
} else if (maxCountIndex === 0) {
neighbourIndex = maxCountIndex + 1;
} else {
a = maxCountIndex - 1;
b = maxCountIndex + 1;
if (beatDistanceCounts[a] > beatDistanceCounts[b]) {
neighbourIndex = a;
} else {
neighbourIndex = b;
}
}
neighbourCount = beatDistanceCounts[neighbourIndex];
divisor = maxCountSoFar + neighbourCount;
if (divisor === 0) {
meanCount = 0;
} else {
meanCount = (maxCountIndex * maxCountSoFar + neighbourIndex * neighbourCount) / divisor;
}
this.bpm = 60 / (meanCount * (samplesPerInstantEnergy / SAMPLE_RATE));
return [meanCount, maxCountIndex];
};
SoundEnergyBeatDetector.prototype._calculateConvolution = function(meanCount, maxCountIndex) {
var b, beatsConvolution, conv, currentConv, espace, i, impulseTrain, j, localMaxPosition, maxConv, maxConvIndex, offsetIndexLeft, offsetIndexRight, ratio, searchForMaxInWindow, _i, _j, _k, _l, _len, _len1, _m, _ref, _ref1, _results;
impulseTrain = [];
espace = 0;
impulseTrain.push(1);
for (i = _i = 1; 1 <= IMPULSE_TRAIN_SIZE ? _i <= IMPULSE_TRAIN_SIZE : _i >= IMPULSE_TRAIN_SIZE; i = 1 <= IMPULSE_TRAIN_SIZE ? ++_i : --_i) {
if (espace >= meanCount) {
impulseTrain.push(1);
espace -= meanCount;
} else {
impulseTrain.push(0);
}
espace += 1;
}
beatsConvolution = [];
this.convolution = [];
maxConv = 0;
maxConvIndex = 0;
for (i = _j = 0, _ref = this.averageEnergies.length - IMPULSE_TRAIN_SIZE - 1; 0 <= _ref ? _j <= _ref : _j >= _ref; i = 0 <= _ref ? ++_j : --_j) {
beatsConvolution[i] = 0;
this.convolution[i] = [this.averageEnergies[i][0], 0];
for (j = _k = 0; 0 <= IMPULSE_TRAIN_SIZE ? _k <= IMPULSE_TRAIN_SIZE : _k >= IMPULSE_TRAIN_SIZE; j = 0 <= IMPULSE_TRAIN_SIZE ? ++_k : --_k) {
this.convolution[i][1] += this.averageEnergies[i + j][1] * impulseTrain[j];
}
currentConv = Math.abs(this.convolution[i][1]);
if (currentConv > maxConv) {
maxConv = currentConv;
maxConvIndex = i;
}
}
ratio = 1 / maxConv;
_ref1 = this.convolution;
for (_l = 0, _len = _ref1.length; _l < _len; _l++) {
conv = _ref1[_l];
conv[1] *= ratio;
}
searchForMaxInWindow = (function(_this) {
return function(offset) {
var maxIndex, maxSoFar, _m, _ref2, _ref3;
maxSoFar = 0;
maxIndex = offset;
for (i = _m = _ref2 = offset - MAX_SEARCH_WINDOW_SIZE, _ref3 = offset + MAX_SEARCH_WINDOW_SIZE; _ref2 <= _ref3 ? _m <= _ref3 : _m >= _ref3; i = _ref2 <= _ref3 ? ++_m : --_m) {
if (i < 0) {
continue;
}
if (i >= _this.convolution.length) {
break;
}
conv = _this.convolution[i][1];
if (conv > maxSoFar) {
maxSoFar = conv;
maxIndex = i;
}
}
return maxIndex;
};
})(this);
beatsConvolution[maxConvIndex] = 1;
offsetIndexRight = maxConvIndex + maxCountIndex;
while (offsetIndexRight < this.convolution.length && this.convolution[offsetIndexRight][1] > 0) {
localMaxPosition = searchForMaxInWindow(offsetIndexRight);
beatsConvolution[localMaxPosition] = 1;
offsetIndexRight = localMaxPosition + maxCountIndex;
}
offsetIndexLeft = maxConvIndex - maxCountIndex;
while (offsetIndexLeft > 0) {
localMaxPosition = searchForMaxInWindow(offsetIndexLeft);
beatsConvolution[localMaxPosition] = 1;
offsetIndexLeft = localMaxPosition - maxCountIndex;
}
this.beats = [];
_results = [];
for (i = _m = 0, _len1 = beatsConvolution.length; _m < _len1; i = ++_m) {
b = beatsConvolution[i];
if (b > 0) {
_results.push(this.beats.push(this.convolution[i][0]));
} else {
_results.push(void 0);
}
}
return _results;
};
return SoundEnergyBeatDetector;
})();
loadAudioFromUrl = function(url, callback) {
var request;
request = new XMLHttpRequest;
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function() {
return callback(request.response);
};
return request.send();
};
this.AbstractAudioSample = (function() {
function AbstractAudioSample() {
this.playing = false;
}
AbstractAudioSample.prototype.loadAudio = function() {
throw 'Load Audio must be implemented by subclass';
};
AbstractAudioSample.prototype.tryPlay = function(offset, gain) {
var gainNode;
if (this.buffer == null) {
return;
}
this.source = this._ctx.createBufferSource();
this.source.buffer = this.buffer;
gainNode = this._ctx.createGain();
if (gain != null) {
gainNode.gain.value = gain;
}
this.source.connect(gainNode);
gainNode.connect(this._ctx.destination);
if ($.isNumeric(offset)) {
this.source.start(0, offset);
} else {
this.source.start(0);
}
return this.playing = true;
};
AbstractAudioSample.prototype.stop = function() {
if (this.source == null) {
return;
}
this.source.stop(0);
return this.playing = false;
};
return AbstractAudioSample;
})();
this.ArrayBufferAudioSample = (function(_super) {
__extends(ArrayBufferAudioSample, _super);
function ArrayBufferAudioSample(_at_arrayBuffer) {
this.arrayBuffer = _at_arrayBuffer;
ArrayBufferAudioSample.__super__.constructor.apply(this, arguments);
}
ArrayBufferAudioSample.prototype.loadAudio = function(_at__ctx, callback) {
this._ctx = _at__ctx;
return this._ctx.decodeAudioData(this.arrayBuffer, (function(_this) {
return function(buffer) {
_this.buffer = buffer;
if (callback != null) {
return callback(_this);
}
};
})(this));
};
return ArrayBufferAudioSample;
})(AbstractAudioSample);
this.PcmAudioGenerator = (function() {
function PcmAudioGenerator() {}
PcmAudioGenerator.prototype.getPcmAudioData = function(offlineContext, audioSample, callback) {
var renderAudioSampleOffline;
renderAudioSampleOffline = (function(_this) {
return function(audioSample) {
offlineContext.oncomplete = function(event) {
return callback(event.renderedBuffer.getChannelData(0));
};
audioSample.tryPlay();
return offlineContext.startRendering();
};
})(this);
return audioSample.loadAudio(offlineContext, renderAudioSampleOffline);
};
return PcmAudioGenerator;
})();
updateSongPlace = function(fractionThroughSong) {
if (audioSample.playing) {
audioSample.stop();
}
trackStartTime = windowStart + ((windowEnd - windowStart) * fractionThroughSong);
return timeline.render(trackStartTime, windowStart, windowEnd);
};
updateVisualisation = function() {
beatDetectorVisualisation.render(beatDetector, windowStart, windowEnd);
return timeline.render(trackStartTime, windowStart, windowEnd);
};
updateBeats = function(pcmAudioData) {
var nOPS, pAEC, pEVC, sPIE;
pAEC = THRESHOLD_CONSTANT;
pEVC = VARIANCE_COEFFICIENT;
sPIE = SAMPLES_PER_INSTANT_ENERGY;
nOPS = NUMBER_OF_PREVIOUS_SAMPLES;
beatDetector = new SoundEnergyBeatDetector();
beatDetector.detectBeats(pcmAudioData, pEVC, pAEC, sPIE, nOPS);
if (windowEnd == null) {
windowEnd = sampleLengthSeconds;
}
console.log("bpm is " + beatDetector.bpm);
$('#bpm').text((beatDetector.bpm.toFixed(2)) + "bpm");
return updateVisualisation();
};
updateAudioFromPcmData = function(pcmAudioData) {
var waveformVisualisation;
waveformVisualisation = new WaveformVisualisation('#waveform', pcmAudioData);
waveformVisualisation.render();
return updateBeats(pcmAudioData);
};
updateAudioFromArrayBuffer = function(arrayBuffer) {
audioContext = getAudioContext();
audioSample = new ArrayBufferAudioSample(arrayBuffer);
return audioSample.loadAudio(audioContext, function(audioSample) {
var length, offlineAudioContext, pcmAudioGenerator, pcmAudioSample;
pcmAudioSample = new ArrayBufferAudioSample(arrayBuffer);
length = audioSample.buffer.length;
sampleLengthSeconds = length / SAMPLE_RATE;
offlineAudioContext = getOfflineAudioContext(CHANNELS, length, SAMPLE_RATE);
pcmAudioGenerator = new PcmAudioGenerator();
return pcmAudioGenerator.getPcmAudioData(offlineAudioContext, pcmAudioSample, updateAudioFromPcmData);
});
};
}).call(this);
I get the following error:
Argument of AudioContext.decodeAudioData can't be a detached buffer
in line:
return this._ctx.decodeAudioData(this.arrayBuffer, (function(_this) {
I tried to shorten down the code as much as possible without removing important information, but please feel free to edit it.
The problem is that you can't use the same ArrayBuffer twice with decodeAudioData(). You can think of this as if decodeAudioData() would consume the ArrayBuffer which means it is gone afterwards. What really happens is that the ArrayBuffer is transferred to another thread by the Web Audio API behind the scenes.
An easy fix would be to copy the ArrayBuffer before you decode it.
new ArrayBufferAudioSample(arrayBuffer.slice(0));
By the way, I've created a simple beat detector called web-audio-beat-detector. Maybe that works for you. Otherwise there is also essentia.js which has a much more elaborate beat detection.
I want to make a dynamic water pipe with water flowing. The front picture is an error message.
The following picture is a method in the method in the vue page. I don't know if it will report an error. If you don't call this method, nothing will be reported.
The following code is the js file that introduces the vue page
//构造函数
function Createline(config) {
this.c = 0;
this.lines = [];
var self = this;
//初始化线
(function (self){
if(config.fx == "w"){
var number = config.canvas_w / (config.width + config.width/2 + config.jiange*2);
number = Math.ceil(number) + 1;
for(var n = 0 ; n < number ; n++){
var mxx = config.canvas_w - n*(config.width+config.width/2+config.jiange*2);
var lines_data = {
mx:mxx,
lx:mxx - config.width,
my:config.my,
ly:config.ly,
vx:config.vx,
vy:config.vy,
}
self.lines.push(lines_data);
var lines_data2 = {
mx:mxx-config.width-config.jiange,
lx:mxx-config.width-config.jiange-config.width/2,
my:config.my,
ly:config.ly,
vx:config.vx,
vy:config.vy,
}
self.lines.push(lines_data2);
}
}
if(config.fx == "h"){
var number = config.canvas_w / (config.width + config.width/2 + config.jiange*2);
number = Math.ceil(number) + 1;
for(var n = 0 ; n < number ; n++){
var myy = config.canvas_h - n*(config.width+config.width/2+config.jiange*2);
var lines_data3 = {
mx:config.mx,
lx:config.lx,
my:myy,
ly:myy - config.width,
vx:config.vx,
vy:config.vy,
}
self.lines.push(lines_data3);
var lines_data4 = {
mx:config.mx,
lx:config.lx,
my:myy-config.width-config.jiange,
ly:myy-config.width-config.jiange-config.width/2,
vx:config.vx,
vy:config.vy,
}
self.lines.push(lines_data4);
}
}
})(self);
}
//开始
Createline.prototype.begin = function (element,config) {
var canvasObj = document.getElementById(element);
var self = this;
var canvas = {
line_w: config.line_w || 3, //线条厚度
vx: config.vx || 0,//x轴运动速度
vy:config.vy || 0,//Y轴速度
color:config.color || "blue",//线条颜色
canvas_w:config.canvas_w || 0,//画布宽度
canvas_h:config.canvas_h || 0,//画布高度
mx: config.mx || 0,//线的开始位置X坐标
my: config.my || 0,//线的开始位置Y坐标
lx: config.lx || 0,//线的结束位置X坐标
ly: config.ly || 0,//线的结束位置Y坐标
fx:config.fx || "w",//运动方向 w 水平 ,h: 垂直
width:config.width || 20,//线的长度
jiange:config.jiange || 10, //线的间隔
}
if(canvasObj.getContext("2d")){
canvas.ctx = canvasObj.getContext("2d"),
canvasObj.width = canvas.canvas_w;
canvasObj.height = canvas.canvas_h;
}else{
console.log("当前环境不支持canvas");
return null;
}
setInterval(function(){
self.createline(self,canvas);
if(canvas.fx == "w"){
self.updateline_w(self,canvas);
}
if(canvas.fx == "h"){
self.updateline_h(self,canvas);
}
self.addline(self,canvas);
},config.time);
return canvas.ctx;
}
//画线
Createline.prototype.createline = function(self,canvas){
var content = canvas.ctx;
content.clearRect(0,0,canvas.canvas_w,canvas.canvas_h);
for(var i = 0 ; i < self.lines.length ; i++){
content.beginPath();
content.moveTo(self.lines[i].mx,self.lines[i].my);
//线经过的转折点或结束点
content.lineTo(self.lines[i].lx,self.lines[i].ly);
//线条宽度
content.lineWidth = canvas.line_w;
content.strokeStyle = canvas.color;
content.stroke();
//线条颜色
content.closePath();
//画线
}
}
//改变数据 水平运动
Createline.prototype.updateline_w = function(self,canvas){
for (var i = 0 ; i < self.lines.length ; i++) {
self.lines[i].mx = self.lines[i].mx + self.lines[i].vx;
self.lines[i].lx = self.lines[i].lx + self.lines[i].vx;
}
//超出画布的线条从数组内删除,减少系统消耗
var cnt = [];
for (var j = 0 ;j < self.lines.length ; j++) {
if(self.lines[j].lx > canvas.canvas_w){
cnt.push(j);
}
}
for(var k = 0; k < cnt.length ; k++){
self.lines.splice(k,1);
}
}
//改变数据 垂直运动
Createline.prototype.updateline_h = function(self,canvas){
for (var i = 0 ; i < self.lines.length ; i++) {
self.lines[i].my = self.lines[i].my + lines[i].vy;
self.lines[i].ly = self.lines[i].ly + lines[i].vy;
}
//超出画布的线条从数组内删除,减少系统消耗
var cnt = [];
for (var j = 0 ;j < self.lines.length ; j++) {
if( self.lines[j].ly > canvas.canvas_h){
cnt.push(j);
}
}
for(var k = 0; k < cnt.length ; k++){
self.lines.splice(k,1);
}
}
//增加线
Createline.prototype.addline = function(self,canvas){
var length = self.lines.length;
//横线
if(canvas.fx == "w"){
//判断是否需要生成线
if(self.lines[length-1].lx > 0){
//起点位置 等于 左右一根线的坐标减去间隔
var ww = self.lines[length - 1].lx - canvas.jiange;
//判断长短
if(self.c == 0){
var jg = canvas.width;
self.c = 10;
}else{
var jg = canvas.width/2;
self.c = 0;
}
var lxx = ww - jg;
var data = {
mx:ww,
lx:lxx,
my:canvas.my,
ly:canvas.ly,
vx:canvas.vx,
vy:canvas.vy,
}
self.lines.push(data);
}
}
//垂直
if(canvas.fx == "h"){
if(length>0){
//判断是否需要生成线
if(self.lines[length-1].ly > 0){
//起点位置 等于 左右一根线的坐标减去间隔
var my = self.lines[length - 1].ly - canvas.jiange;
//判断长短
if(c == 0){
var jg = canvas.width;
c = 10;
}else{
var jg = canvas.width/2;
c = 0;
}
var ly = my - jg;
var data = {
mx:canvas.mx,
lx:canvas.lx,
my:my,
ly:ly,
vx:canvas.vx,
vy:canvas.vy,
}
self.lines.push(data);
}
}else{
var data = {
mx:canvas.mx,
my:canvas.my,
lx:canvas.lx,
ly:canvas.ly,
vx:canvas.vx,
vy:canvas.vy,
}
self.lines.push(data);
}
}
}
export{
Createline
}
You need to use new when creating a instance for a constructor that uses this:
var cre = new Createline(data);
When a function is executed using new it does the following:
function User(name) {
// this = {}; (implicitly)
// add properties to this
this.name = name;
this.isAdmin = false;
// return this; (implicitly)
}
I found this code for a slot machine and have been playing around with it. I'm having trouble removing the pull event from everything but the handle. Anybody see what I might be missing? I'm trying to enable a click trigger for some html but it also triggers the whole event from restarting. Any ideas on why that might be happening as well?
var fps = 60;
window.raf = (function(){
return requestAnimationFrame || webkitRequestAnimationFrame || mozRequestAnimationFrame || function(c){setTimeout(c,1000/fps);};
})();
/*--------------=== Slot machine definition ===--------------*/
(function() {
var NAME = "SlotMachine",
defaultSettings = {
width : "600",
height : "600",
colNum : 3,
rowNum : 9,
winRate : 50,
autoPlay : true,
autoSize : false,
autoPlayTime : 10,
layout : 'compact',
handleShow : true,
handleWidth : 35,
handleHeight : 30,
machineBorder : 15,
machineColor : 'rgba(120,60,30,1)',
names : [
"seven",
"lemon",
"cherry",
"watermelon",
"banana",
"bar",
"prune",
"bigwin",
"orange"
]
},
completed = true,
isShuffle = true,
supportTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints,
firstTime = true,
nextLoop = null ;
SlotMachine = function (argument) {
this.init = this.init.bind(this);
this.run = this.run.bind(this);
this.addListener = this.addListener.bind(this);
this.beforeRun = this.beforeRun.bind(this);
this.afterRun = this.afterRun.bind(this);
this.showWin = this.showWin.bind(this);
this.rotateHandle = this.rotateHandle.bind(this);
this.colArr = [];
this.options = {};
}
SlotMachine.prototype.beforeRun = function(){
if (completed) {
this.showWin(false);
completed = false;
var result = null;
result = this.options.names[random(this.options.rowNum*100/this.options.winRate)|0];//set winrate
for(var i=0;i<this.options.colNum;i++){
this.colArr[i].beforeRun(result);
}
this.rotateHandle();
this.run();
}
if (this.options.autoPlay) nextLoop = setTimeout(function(){this.beforeRun()}.bind(this),this.options.autoPlayTime*1000);
}
SlotMachine.prototype.afterRun = function(){
completed = true;
var results = [],win=true;
for(var i=0;i<this.options.colNum;i++){
results.push(this.colArr[i].getResult());
if (i>0 && results[i]!=results[i-1]) {
win = false;
break;
}
}
if(win){
this.showWin(true);
setTimeout(function(){
this.showWin(false);
}.bind(this),this.options.autoPlayTime*1000);
}
}
SlotMachine.prototype.rotateHandle = function(){
var handle = document.querySelector(".handle");
if (handle) {
handle.addClass("active");
setTimeout(function(){
handle.removeClass("active");
},1000);
}
}
SlotMachine.prototype.run = function(){
var done = true;
for(var i=0;i<this.options.colNum;i++){
done &= this.colArr[i].run();
}
if (!done) raf(this.run)
else this.afterRun();
}
SlotMachine.prototype.showWin = function(show){
var winner = document.querySelector(".winner");
if (winner) winner.className= show ? "winner active" : "winner";
}
SlotMachine.prototype.init = function(){
//reset all
completed = true;
clearTimeout(nextLoop);
//get settings
var BannerFlow = arguments[0],
settingStyle = "",
machine = document.querySelector(".machine"),
container = document.querySelector(".container");
machine.style.opacity = 0;
for(var key in defaultSettings) {
this.options[key] = defaultSettings[key];
}
if (BannerFlow!==undefined){
var settings = BannerFlow.settings;
this.options.winRate = settings.winRate ? settings.winRate : defaultSettings.winRate;
this.options.autoPlay = settings.autoPlay;
this.options.colNum = settings.numberColumn ? settings.numberColumn : defaultSettings.colNum;
this.options.layout = settings.layout ? settings.layout : defaultSettings.layout;
this.options.machineColor = settings.machineColor ? settings.machineColor : defaultSettings.machineColor;
this.options.machineBorder = settings.machineBorder>=0 ? settings.machineBorder : defaultSettings.machineBorder;
this.options.height = settings.height ? settings.height : defaultSettings.height;
this.options.width = settings.width ? settings.width : defaultSettings.width;
this.options.autoSize = settings.autoSize;
if (this.options.autoSize) {
this.options.height = window.innerHeight;
this.options.width = window.innerWidth;
}
this.options.handleShow = settings.handleShow;
this.options.handleWidth = this.options.handleShow ? defaultSettings.handleWidth : 0;
this.options.autoPlayTime = settings.autoPlayTime ? settings.autoPlayTime : defaultSettings.autoPlayTime;
this.options.customImage = settings.customImage;
}
//apply settings
if (this.options.customImage){
var urls = BannerFlow.text.strip().split(",");
this.options.names = [];
for(var i=0;i<urls.length;i++){
var name = "image-"+i ; urls[i];
this.options.names.push(name);
settingStyle += getStyle("."+name+":after",{
"background-image" : "url('"+urls[i]+"')"
});
}
}
settingStyle += getStyle(".machine",{
"margin-top" : (window.innerHeight - this.options.height)/2 +"px",
"margin-left" : (window.innerWidth - this.options.width)/2 +"px"
});
settingStyle += getStyle(".container",{
"height" : this.options.height+"px",
"width" : this.options.width - this.options.handleWidth +"px",
"border-width" : this.options.machineBorder + "px",
"border-color" : this.options.machineColor + " " + getLighter(this.options.machineColor)
});
var winnerSize = 1.2*Math.max(this.options.height,this.options.width);
settingStyle += getStyle(".winner:before,.winner:after",{
"height" : winnerSize+"px",
"width" : winnerSize+"px",
"top" : (this.options.height-winnerSize)/2 - 20 + "px",
"left" : (this.options.width-winnerSize)/2 - this.options.handleWidth + "px"
});
settingStyle += getStyle(".handle",{
"margin-top" : this.options.height/2-this.options.handleHeight+"px"
});
document.querySelector("#setting").innerHTML = settingStyle;
//remove old cols
if (this.colArr && this.colArr.length > 0)
for (var i=0;i<this.colArr.length;i++){
container.removeChild(this.colArr[i].getDOM());
}
this.colArr = [];
// add new cols
for(var i=0;i<this.options.colNum;i++){
var col = new SlotColumn();
col.init(this.options.names.slice(0,this.options.rowNum),isShuffle);
this.colArr.push(col);
document.querySelector(".container").appendChild(col.getDOM());
}
machine.style.opacity = "1";
}
SlotMachine.prototype.addListener = function(){
var BannerFlow=arguments[0],timer,
that = this ,
container = document.querySelector(".container");
if (typeof BannerFlow!= 'undefined') {
// BannerFlow event
BannerFlow.addEventListener(BannerFlow.RESIZE, function() {
//clearTimeout(timer);
//timer = setTimeout(function(){that.init(BannerFlow);that.beforeRun()},500);
});
BannerFlow.addEventListener(BannerFlow.CLICK, function() {
that.beforeRun();
});
} else {
// Window event
window.addEventListener('resize', function(){
//clearTimeout(timer);
//timer = setTimeout(function(){that.init(BannerFlow);that.beforeRun()},500)
});
if (supportTouch) {
window.addEventListener("touchstart",function(){
that.beforeRun();
});
} else {
window.addEventListener("click",function(){
that.beforeRun();
});
}
}
var slotTrigger = document.querySelector("#slot-trigger");
if (slotTrigger) {
slotTrigger.addEventListener("click",function(e){
this.addClass('slot-triggerDown');
})
}
}
window[NAME]= SlotMachine;
})();
/*--------------=== Slot Column definition ===--------------*/
(function(){
var NAME = "SlotColumn";
SlotColumn = function(){
this.col = document.createElement("div");
this.col.className = "col";
this.init = this.init.bind(this);
this.run = this.run.bind(this);
this.beforeRun = this.beforeRun.bind(this);
this.getResult = this.getResult.bind(this);
this.getDOM = this.getDOM.bind(this);
this.arr = [];
this.colHeight=this.rowHeight=0;
this.loop = 2;
}
SlotColumn.prototype.init = function(){
this.col.empty();
this.arr=arguments[0];
var isShuffle=arguments[1];
if(isShuffle) shuffle(this.arr);
for(var i=0; i<this.arr.length*this.loop;i++){
var row = document.createElement("div");
row.className = "row "+this.arr[i%this.arr.length];
this.col.appendChild(row);
}
this.top = 0;
}
SlotColumn.prototype.beforeRun = function(){
this.halfHeight = this.col.offsetHeight/this.loop;
this.colHeight = this.col.scrollHeight/2;
this.rowHeight = this.colHeight/this.arr.length;
this.nextResult = arguments[0];
var next = this.arr.indexOf(this.nextResult);
if (next==-1) next=random(0,this.arr.length-1)|0;
var s = this.top + (random(2,10)|0)*this.colHeight + ((next+0.5)*this.rowHeight|0) - this.halfHeight;
var n = (random(2,6)|0) * fps;
this.speed = 2*s/(n+1);
this.acceleration = this.speed/n;
}
SlotColumn.prototype.getResult = function(){
var result = Math.ceil(((this.halfHeight-this.top)%this.colHeight)/this.rowHeight)-1;
//console.log(this.top,result,this.arr[result],this.halfHeight,this.colHeight,this.rowHeight);
return this.arr[result];
}
SlotColumn.prototype.run = function(){
if(this.speed <= 0) return true;//completed
this.top = (this.top - this.speed) % this.colHeight;
this.speed -= this.acceleration;
this.top %= this.colHeight;
this.col.style.transform = "translateY("+this.top+"px)";
return false;//not completed
}
SlotColumn.prototype.getDOM = function(){
return this.col;
}
window[NAME] = SlotColumn;
}());
/*--------------=== Utils definition ===--------------*/
//random in range
var random = function(){
var isNumeric = function(n){return !isNaN(parseFloat(n)) && isFinite(n)},
val = Math.random(),
arg = arguments;
return isNumeric(arg[0]) ? isNumeric(arg[1]) ? arg[0] + val*(arg[1] - arg[0]) : val*arg[0] : val;
};
//shuffle an array
var shuffle = function(arr){
var j,tmp;
for(var i=0;i<arr.length;i++){
j = random(arr.length)|0;
tmp = arr[i];arr[i]=arr[j];arr[j]=tmp;
}
}
//get CSS3 style
var setStyleCss3 = function (object, key, value) {
object.style['-webkit-'+ key] = value;
object.style['-moz-'+key] = value;
object.style['-ms-'+key] = value;
object.style[key] = value;
}
//get name from url
var getNameFromUrl = function(url){
if (url) {
var s=url.lastIndexOf("/")+1,e =url.lastIndexOf(".");
return s<e ? url.substring(s,e) : "";
}
return "";
}
//get style from object style
var getStyle = function(selector,styleObj){
var isAttribute = false;
var newStyle = selector+"{";
for(var attr in styleObj) {
if (styleObj[attr]) {
isAttribute = true;
newStyle += attr+" : "+styleObj[attr]+";";
}
}
newStyle+="}";
return isAttribute ? newStyle : "";
}
// get lighter color from rgba colors
var getLighter = function(rgba){
var o = /[^,]+(?=\))/g.exec(rgba)[0]*0.75;
return rgba.replace(/[^,]+(?=\))/g,o);
}
//remove html from text
if (!String.prototype.strip) {
String.prototype.strip = function() {
return this.replace(/(<[^>]+>)/ig," ").trim();
}
}
//remove all child node
if (!Node.prototype.empty) {
Node.prototype.empty = function(){
while (this.firstChild) {
this.removeChild(this.firstChild);
}
}
}
if (!HTMLElement.prototype.hasClass) {
Element.prototype.hasClass = function(c) {
return (" "+this.className+" ").replace(/[\n\t]/g, " ").indexOf(" "+c+" ") > -1;
}
}
if (!HTMLElement.prototype.addClass) {
HTMLElement.prototype.addClass = function(c) {
if (!this.hasClass(c)) this.className += (" " +c);
return this;
}
}
if (!HTMLElement.prototype.removeClass) {
HTMLElement.prototype.removeClass = function(c) {
if (this.hasClass(c)) this.className = (" "+this.className+" ").replace(" "+c+" "," ").trim();
return this;
}
}
/*--------------=== Main function ===--------------*/
var timer,widget = null;
if (typeof BannerFlow != 'undefined') {
BannerFlow.addEventListener(BannerFlow.SETTINGS_CHANGED, function() {
clearTimeout(timer);
timer = setTimeout(function(){
if (widget==null) {
widget = new SlotMachine();
widget.addListener(BannerFlow);
}
widget.init(BannerFlow);
widget.beforeRun();
},500);
});
}else {
window.addEventListener("load",function(e){
if (widget==null) {
widget = new SlotMachine();
widget.addListener();
}
widget.init();
widget.beforeRun();
})
}
I'm trying to make a cross fade inside a bootstrap grid duplicating the images.
The problem is the images don't stack over each other and it sometimes creates new images - however it doesn't delete them.
var tempo1 = setInterval(rand, 6000);
function rand() {
var imagensTroca = 5;
var grupos = [
['DFLO_0005', 'DFLO_0030', 'DFLO_0042', 'DFLO_0068', 'DFLO_0084'],
['DANI_0004', 'DANI_0012', 'DANI_0020', 'DANI_0027'],
['DCAV_0003', 'DCAV_0017', 'DCAV_0024'],
['DCOR_0029', 'DCOR_0010', 'DCOR_0001'],
['DETI_0004', 'DETI_0002', 'DETI_0007'],
['DGEO_0002', 'DGEO_0009', 'DGEO_0001'],
['DIND_0001', 'DIND_0006', 'DIND_0012'],
['DOLD_0001', 'DOLD_0002', 'DOLD_0008'],
['DPSI_0006', 'DPSI_0008', 'DPSI_0013'],
['DUNI_0025', 'DUNI_0031', 'DUNI_0032'],
['DVIN_0002', 'DVIN_0014', 'DVIN_0016'],
['DXAD_0001', 'DXAD_0002', 'DXAD_0014'],
['DFRA_0004', 'DFRA_0006', 'DFRA_0007'],
['DOLH_0002', 'DOLH_0003', 'DOLH_0004'],
['DYIN_0011', 'DYIN_0005', 'DYIN_0009']
];
var dCheck = [];
for (i = 0; i < imagensTroca; i++) {
var parteDoArray = Math.floor(Math.random() * grupos.length);
var divisaoDoArray = grupos[parteDoArray].toString();
var selecaoDaDivisao = divisaoDoArray.split(",");
var imagemEscolhida = selecaoDaDivisao[Math.floor(Math.random() * selecaoDaDivisao.length)];
var img = document.getElementById('cat' + parteDoArray);
var cacheDaImagem = img.src;
if (dCheck[0] != parteDoArray) {
var newImg = new Image();
newImg.src = cacheDaImagem;
$('#box' + parteDoArray).append(newImg);
newImg.id = "clone" + parteDoArray.toString();
newImg.className += "img-circle img-clone";
dCheck[0] = parteDoArray;
change1(imagemEscolhida, parteDoArray);
}
}
}
function change1(_loc1, _loc2) {
$(document).ready(function() {
var img2 = document.getElementById('cat' + _loc2);
img2.src = 'http://www.alargs.com/themes/theshop/img/jumbotron/' + _loc1 + '.jpg';
img2.hide();
$('#clone' + _loc2).stop(true).fadeOut(1000, function() {
$(this).remove();
});
$('#cat' + _loc2).fadeIn(1000);
});
}
https://jsfiddle.net/f8xaey4t/
I researched by myself and made it so if someone else need it it's here:
var tempo1 = setInterval(rand,3000);
function rand() {
var imagensTroca = 6;
var grupos = [['DFLO_0005','DFLO_0030','DFLO_0042','DFLO_0068','DFLO_0084'],['DANI_0004','DANI_0012','DANI_0020','DANI_0027'],['DCAV_0003','DCAV_0017','DCAV_0024'],['DCOR_0029','DCOR_0010','DCOR_0001'],['DETI_0004','DETI_0002','DETI_0007'],['DGEO_0002','DGEO_0009','DGEO_0001'],['DIND_0001','DIND_0006','DIND_0012'],['DOLD_0001','DOLD_0002','DOLD_0008'],['DPSI_0006','DPSI_0008','DPSI_0013'],['DUNI_0025','DUNI_0031','DUNI_0032'],['DVIN_0002','DVIN_0014','DVIN_0016'],['DXAD_0001','DXAD_0002','DXAD_0014'],['DFRA_0004','DFRA_0006','DFRA_0007'],['DOLH_0002','DOLH_0003','DOLH_0004'],['DYIN_0011','DYIN_0005','DYIN_0009']];
var dCheck = [];
var dCheck2 = [];
for( i=0; i < imagensTroca; i++){
var parteDoArray = Math.floor(Math.random() * grupos.length);
var divisaoDoArray = grupos[parteDoArray].toString();
var selecaoDaDivisao = divisaoDoArray.split(",");
var imagemEscolhida = selecaoDaDivisao[Math.floor(Math.random() * selecaoDaDivisao.length)];
var img = document.getElementById('cat' + parteDoArray);
var cacheDaImagem = img.src;
if(check(dCheck,imagemEscolhida)===true && check(dCheck2,parteDoArray)===true){
var newImg = new Image();
newImg.src = cacheDaImagem;
$('#box'+parteDoArray).append(newImg);
newImg.id = "clone"+parteDoArray;
newImg.className += "img-circle img-clone";
dCheck[i] = imagemEscolhida;
dCheck2[i] = parteDoArray;
change1(imagemEscolhida,parteDoArray);
$('#clone'+parteDoArray).fadeOut(1000,function() { for( k=0; k<= grupos.length; k++){ $('#clone'+k).remove();}});
$('#cat'+parteDoArray).fadeIn(1000);
}
}
}
function change1(_loc1,_loc2){
$(document).ready(function () {
var img2 = document.getElementById('cat'+_loc2);
img2.src = 'themes/theshop/img/jumbotron/' + _loc1 + '.jpg';
});
}
function check(arr,test) {
var i;
for (i = 0; i <= arr.length; i++) {
if (arr[i] === test) {
return false;
}
}
In the following code, the console.log from the line 92 and 93 (switchPosition function) doesn't provide the same result. I am, I have to admit, stuck.
Any thoughts, ideas, explanations?
Thanks you for the time taken,
var Carrousel = (function () {
var self = {},
config = {
item: 3, // item to be displayed
scroll: 2 // number of items to be scrolled
},
container = null,
items = null,
nbItems = null,
nbSlide = null,
position = [],
searchPositionDepending = []
;
self.init = function (opts) {
options = opts || {}
execute();
}
// Private Method
execute = function () {
container = document.getElementById('carrousel');
items = document.getElementsByClassName('flux');
var containerWidth = container.offsetWidth;
var containerHeight = items[0].offsetHeight * 1.5;
container.style.height = '' + containerHeight + '';
nbItems = document.getElementsByClassName('flux').length;
nbSlide = nbItems / config.item;
// Initialisation du Carrousel
for (i = 0; i < nbItems; i++) {
items[i].style.width = '' + (containerWidth / config.item) + '';
items[i].style.display = 'none';
items[i].style.position = 'absolute';
items[i].style.top = "0";
items[i].style.left = "0";
items[i].setAttribute('data-position', '' + (i + 1) + '');
items[i].setAttribute('data-number', '' + i + '');
}
searchPosition();
placement();
document.getElementById('next').onclick = function () {
next();
searchPosition();
switchPosition();
placement();
// position();
}
document.getElementById('previous').onclick = function () {
// previous();
// searchPosition();
// position();
}
}
searchPosition = function () {
for (i = 0; i < config.item; i++) {
searchPositionDepending[i] = (function () { //Appending a function searchPosition per item displayed
for (j = 1; j <= config.item; j++) { //Passing through the first items to get their position
if (items[i].dataset.position === '' + j + '') {
position[j] = items[i];
return true;
} else {
}
}
;
})();
}
}
switchPosition = function () {
for (i = 0, j = 0; i < nbItems; i++, j++) {
if (items[i].dataset.position === '' + j + '') {
position[i] = position[i];
}
}
for (i = config.item, j = 1; i < nbItems; i++, j++) { //Replacing in good order, except for the one that are place into the variable position[];
items[i] = items[j];
}
for (i = (nbItems - config.item +1), j = 1; i <= nbItems; i++, j++) { //Replacing in good order the one that are place into variable position[];
items[i] = position[j];
console.log(i);
console.log(j);
console.log(position[j]);
console.log(items[i]);
}
for (i = 0; i < nbItems; i++) {
console.log(items[i]);
}
console.log(items[10]);
}
placement = function () {
for (i = 1; i <= config.item; i++) {
var rect = items[0].getBoundingClientRect();
item_width = Math.floor(rect.width);
item_height = Math.floor(rect.height);
var x = item_width * (i - 1) * 1.1;
items[i].style.display = 'block';
items[i].style.transform = "translate3D(" + x + "px, 0px, 0px)";
}
}
next = function () {
for (i = config.item, j = 1; i < nbItems; i++, j++) { //Updating all dataset position, except for the items that are displayed.
items[i].dataset.position = j;
}
for (i = 1, j = 2; i <= config.item; i++, j--) {
position[i].dataset.position = nbItems - j;
position[i].style.display = "none";
}
}
return self;
})();