How to connect a JQuery UI slider & a Web Audio API gainNode - web-audio-api

I am trying to connect a JQuery slider to a gain node of an oscillator using the Web Audio API.
The oscillator works, there is a working gain slider and there is a JQuery slider. I want the JQuery slider to control the gain like the other slider does.
Here is the code thus far
http://jsfiddle.net/taoist/JCTJj/2/

Here you go: http://jsfiddle.net/JCTJj/19/
$(function() {
var webSlider = document.getElementById('volume');
var output = $('#gain');
var sliderParams = {
'orientation': "vertical",
'range': "min",
'min': 0,
'max': 1,
'animate': false,
'step': 0.01,
'slide': function(event, ui) {
window.gainNode.gain.value = ui.value;
output.val(window.gainNode.gain.value);
},
'stop': function(event, ui) {
console.log(window.gainNode.gain.value);
}
};
$('#sliderOne').slider(sliderParams);
webSlider.addEventListener('change', function () {
window.gainNode.gain.value = this.value;
output.val(window.gainNode.gain.value);
});
});

your gain function should recieve a value like
function gain(value) {
gainNode.gain.value = value;
}
Then in jQuery slider do something like
slide: function( event, ui ) {
gain(ui.value);
}

Related

Want counter to start counting only when in viewport

I have a counter that is working perfectly fine, but it starts counting when the page is loaded - meaning that often when the user scrolls down, the counter has already stoped and therefore the effect is lost.
I've tried multiple suggestions found here on Stack Overflow, but none worked for my specific case.
Here's my code:
$('.counter').each(function() {
var $this = $(this),
countTo = $this.attr('data-count');
$({ countNum: $this.text()}).animate({
countNum: countTo
},
{
duration: 9000,
easing:'linear',
step: function() {
$this.text(Math.floor(this.countNum));
},
complete: function() {
$this.text(this.countNum);
//alert('finished');
}
});
});
Any tips I could incorporate into my code to ensure the counter starts counting only in viewport?
Many thanks in advance!
A good way to achive is using IntersectionObserver https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API to active your countdown when in viewport.
First, create an observer that will trigger when in view, and then start countdown.
const options = {
root: null,
rootMargin: '0px', //this determines when observer will trigger
threshold: 0
}
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if(entry.isIntersecting) {
observer.unobserve(entry.target)
activateCountdown(entry.target) //call you function that will activate the counter
}
})
}, options);
Now, apply this observer to all counter that exists
$('.counter').each((index, element) => {
observer.observe(element)
})
function activateCountdown(countdown) {
//here you activate your counter
}

Mapbox GL JS: Style is not done loading

I have a map wher we can classically switch from one style to another, streets to satellite for example.
I want to be informed that the style is loaded to then add a layer.
According to the doc, I tried to wait that the style being loaded to add a layer based on a GEOJson dataset.
That works perfectly when the page is loaded which fires map.on('load') but I get an error when I just change the style, so when adding layer from map.on('styledataloading'), and I even get memory problems in Firefox.
My code is:
mapboxgl.accessToken = 'pk.token';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v10',
center: [5,45.5],
zoom: 7
});
map.on('load', function () {
loadRegionMask();
});
map.on('styledataloading', function (styledata) {
if (map.isStyleLoaded()) {
loadRegionMask();
}
});
$('#typeMap').on('click', function switchLayer(layer) {
var layerId = layer.target.control.id;
switch (layerId) {
case 'streets':
map.setStyle('mapbox://styles/mapbox/' + layerId + '-v10');
break;
case 'satellite':
map.setStyle('mapbox://styles/mapbox/satellite-streets-v9');
break;
}
});
function loadJSON(callback) {
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', 'regions.json', true);
xobj.onreadystatechange = function () {
if (xobj.readyState == 4 && xobj.status == "200") {
callback(xobj.responseText);
}
};
xobj.send(null);
}
function loadRegionMask() {
loadJSON(function(response) {
var geoPoints_JSON = JSON.parse(response);
map.addSource("region-boundaries", {
'type': 'geojson',
'data': geoPoints_JSON,
});
map.addLayer({
'id': 'region-fill',
'type': 'fill',
'source': "region-boundaries",
'layout': {},
'paint': {
'fill-color': '#C4633F',
'fill-opacity': 0.5
},
"filter": ["==", "$type", "Polygon"]
});
});
}
And the error is:
Uncaught Error: Style is not done loading
at t._checkLoaded (mapbox-gl.js:308)
at t.addSource (mapbox-gl.js:308)
at e.addSource (mapbox-gl.js:390)
at map.js:92 (map.addSource("region-boundaries",...)
at XMLHttpRequest.xobj.onreadystatechange (map.js:63)
Why do I get this error whereas I call loadRegionMask() after testing that the style is loaded?
1. Listen styledata event to solve your problem
You may need to listen styledata event in your project, since this is the only standard event mentioned in mapbox-gl-js documents, see https://docs.mapbox.com/mapbox-gl-js/api/#map.event:styledata.
You can use it in this way:
map.on('styledata', function() {
addLayer();
});
2. Reasons why you shouldn't use other methods mentioned above
setTimeout may work but is not a recommend way to solve the problem, and you would got unexpected result if your render work is heavy;
style.load is a private event in mapbox, as discussed in issue https://github.com/mapbox/mapbox-gl-js/issues/7579, so we shouldn't listen to it apparently;
.isStyleLoaded() works but can't be called all the time until style is full loaded, you need a listener rather than a judgement method;
Ok, this mapbox issue sucks, but I have a solution
myMap.on('styledata', () => {
const waiting = () => {
if (!myMap.isStyleLoaded()) {
setTimeout(waiting, 200);
} else {
loadMyLayers();
}
};
waiting();
});
I mix both solutions.
I was facing a similar issue and ended up with this solution:
I created a small function that would check if the style was done loading:
// Check if the Mapbox-GL style is loaded.
function checkIfMapboxStyleIsLoaded() {
if (map.isStyleLoaded()) {
return true; // When it is safe to manipulate layers
} else {
return false; // When it is not safe to manipulate layers
}
}
Then whenever I swap or otherwise modify layers in the app I use the function like this:
function swapLayer() {
var check = checkIfMapboxStyleIsLoaded();
if (!check) {
// It's not safe to manipulate layers yet, so wait 200ms and then check again
setTimeout(function() {
swapLayer();
}, 200);
return;
}
// Whew, now it's safe to manipulate layers!
the rest of the swapLayer logic goes here...
}
Use the style.load event. It will trigger once each time a new style loads.
map.on('style.load', function() {
addLayer();
});
My working example:
when I change style
map.setStyle()
I get error Uncaught Error: Style is not done loading
This solved my problem
Do not use map.on("load", loadTiles);
instead use
map.on('styledata', function() {
addLayer();
});
when you change style, map.setStyle(), you must wait for setStyle() finished, then to add other layers.
so far map.setStyle('xxx', callback) Does not allowed. To wait until callback, work around is use map.on("styledata"
map.on("load" not work, if you change map.setStyle(). you will get error: Uncaught Error: Style is not done loading
The current style event structure is broken (at least as of Mapbox GL v1.3.0). If you check map.isStyleLoaded() in the styledata event handler, it always resolves to false:
map.on('styledata', function (e) {
if (map.isStyleLoaded()){
// This never happens...
}
}
My solution is to create a new event called "style_finally_loaded" that gets fired only once, and only when the style has actually loaded:
var checking_style_status = false;
map.on('styledata', function (e) {
if (checking_style_status){
// If already checking style status, bail out
// (important because styledata event may fire multiple times)
return;
} else {
checking_style_status = true;
check_style_status();
}
});
function check_style_status() {
if (map.isStyleLoaded()) {
checking_style_status = false;
map._container.trigger('map_style_finally_loaded');
} else {
// If not yet loaded, repeat check after delay:
setTimeout(function() {check_style_status();}, 200);
return;
}
}
I had the same problem, when adding real estate markers to the map. For the first time addding the markers I wait till the map turns idle. After it was added once I save this in realEstateWasInitialLoaded and just add it afterwards without any waiting. But make sure to reset realEstateWasInitialLoaded to false when changing the base map or something similar.
checkIfRealEstateLayerCanBeAddedAndAdd() {
/* The map must exist and real estates must be ready */
if (this.map && this.realEstates) {
this.map.once('idle', () => {
if (!this.realEstateWasInitialLoaded) {
this.addRealEstatesLayer();
this.realEstateWasInitialLoaded = true
}
})
if(this.realEstateWasInitialLoaded) {
this.addRealEstatesLayer();
}
}
},
I ended up with :
map.once("idle", ()=>{ ... some function here});
In case you have a bunch of stuff you want to do , i would do something like this =>
add them to an array which looks like [{func: function, param: params}], then you have another function which does this:
executeActions(actions) {
actions.forEach((action) => {
action.func(action.params);
});
And at the end you have
this.map.once("idle", () => {
this.executeActions(actionsArray);
});
I have created simple solution. Give 1 second for mapbox to load the style after you set the style and you can draw the layer
map.setStyle(styleUrl);
setTimeout(function(){
reDrawMapSourceAndLayer(); /// your function layer
}, 1000);
when you use map.on('styledataloading') it will trigger couple of time when you changes the style
map.on('styledataloading', () => {
const waiting = () => {
if (!myMap.isStyleLoaded()) {
setTimeout(waiting, 200);
} else {
loadMyLayers();
}
};
waiting();
});

Ionic Decrease Scroll Speed

i got problem when trying to slowing scroll from this code :
$ionicScrollDelegate.$getByHandle('credit').scrollBottom(true).
How can i slowing down the scroll? Because now it scrolling too fast for me. I need to slowing down the scroll, just like credit scene on the Star Wars movie.
Anyhelp would be much appreciated, thanks!
$scope.viewCreditsV2 = function () {
$ionicModal.fromTemplateUrl('views/popupcredit.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modal = modal;
$scope.modal.show();
if ($scope.modal.isShown()){
setTimeout(function() {
// Do something after 2 seconds
$ionicScrollDelegate.$getByHandle('credit').scrollBottom(true);
}, 2000);
}
});
$scope.openModal = function() {
$scope.modal.show();
};
$scope.closeModal = function() {
// $scope.modal.hide();
$scope.modal.remove();
};
};
This question is old but somebody might use it.
Even though there are no parameters to pass options, you can still access the ScrollView object using the $ionScrollDelegate.
Following #Jeremy Wilken's answer (which helped me derive this one), you could do:
$timeout(function() {
$ionicScrollDelegate.getScrollView().options.animationDuration = 400;
console.log($ionicScrollDelegate.getScrollView().options);
});
//.....
$ionicScrollDelegate.scrollBy(0,20, true) // Animation will be slower now
I wrapped the call on a $timeout to avoid racing conditions from $ionicScrollDelegate not being created.
Ionic doesn't have a means to change the animation speed for the $ionicScrollDelegate. There is no public API to make this change.
https://github.com/driftyco/ionic/blob/master/js/views/scrollView.js#L327
You can use $anchorScroll as shown in the Angular documentation https://docs.angularjs.org/api/ng/service/$anchorScroll

kendo-ui autocomplete extend

I'm trying to extend the kendo-ui autocomplete control: I want the search start when te user hit enter, so basically I've to check the user input on keydown event.
I've tried to catch the keydown event with this code:
(function($) {
ui = kendo.ui,
Widget = ui.Widget
var ClienteText = ui.AutoComplete.extend({
init: function(element,options) {
var that=this;
ui.AutoComplete.fn.init.call(this, element, options);
$(this).bind('keydown',function(e){ console.log(1,e); });
$(element).bind('keydown',function(e){ console.log(2,e); });
},
options: {
[...list of my options...]
},
_keydown: function(e) {
console.log(3,e);
kendo.ui.AutoComplete.fn._keydown(e);
}
});
ui.plugin(ClienteText);
})(jQuery);
None of the binded events gets called, only the _keydown, and then I'm doing something wrong and cannot call the autocomplete "normal" keydown event.
I've seen a lot of examples that extend the base widget and then create a composite widget, but I'm not interested in doing that, I only want to add a functionality to an existing widget.
Can someone show me what I'm doing wrong?
Thank you!
What about avoiding the extend and take advantage of build in options and methods on the existing control : http://jsfiddle.net/vojtiik/Vttyq/1/
//create AutoComplete UI component
var complete = $("#countries").kendoAutoComplete({
dataSource: data,
filter: "startswith",
placeholder: "Select country...",
separator: ", ",
minLength: 50 // this is to be longer than your longest char
}).data("kendoAutoComplete");
$("#countries").keypress(function (e) {
if (e.which == 13) {
complete.options.minLength = 1; // allow search
complete.search($("#countries").val());
complete.options.minLength = 50; // stop the search again
}
});
This code actually work:
(function($) {
ui = kendo.ui,
ClienteText = ui.AutoComplete.extend({
init: function(element,options) {
ui.AutoComplete.fn.init.call(this, element, options);
$(element).bind('keydown',function(e){
var kcontrol=$(this).data('kendoClienteText');
if (e.which === 13) {
kcontrol.setDataSource(datasource_clientes);
kcontrol.search($(this).val());
} else {
kcontrol.setDataSource(null);
}
});
},
options: {
name: 'ClienteText',
}
});
ui.plugin(ClienteText);
})(jQuery);
but I don't know if it's the correct way to do it.

backstretch next & previous buttons

I'm using the backstretch jquery to cycle images on my website. I can get the images to cycle fine, but I'm trying to add "next" and "previous" buttons, and I can't get them to work.
When I click on the next button, nothing happens.
My next button looks like this:
<a id="next" href="#"><img src="/images/arrow-right.png">
And I'm putting all my jquery code at the bottom of the page before the body close tag.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="/js/jquery.backstretch.js"></script>
<script>
var images = [
"/images/backgrounds/Image01.jpg",
"/images/backgrounds/Image02.jpg",
"/images/backgrounds/Image03.jpg"
];
$(images).each(function() {
$('<img/>')[0].src = this;
});
// The index variable will keep track of which image is currently showing
var index = 0;
$.backstretch(images[index], {speed: 500});
$('#next').click(function(x) {
x.preventDefault();
$('body').data('backstretch').next();
});
$('#prev').click(function(x) {
x.preventDefault();
$('body').data('backstretch').prev();
});
</script >
Using firebug to debug, I get this:
TypeError: $(...).data(...) is undefined
$('body').data('backstretch').next();
The backstretch call was wrong.
Instead of this:
$.backstretch(images[index], {speed: 500});
I wanted this:
$.backstretch(images, {speed: 500});
$('body').data('backstretch').pause();
It's not that the next/prev buttons weren't working, it's that the initial call was passing the first image, not the set of images.
The second line (w/ pause in it) is there so the images don't change automatically, they only change when I hit the next/prev buttons.
Try this instead:
$('body').backstretch(images[index], {speed: 500});
$('#next').click(function(x) {
x.preventDefault();
$('body').data('backstretch').next();
});
$('#prev').click(function(x) {
x.preventDefault();
$('body').data('backstretch').prev();
});
I'm trying to fix this same problem of yours...
var rootUrl = "http://www.sagmeisterwalsh.com";
var images = [
rootUrl+"/images/u_work/Aizone-11.jpg",
rootUrl+"/images/u_work/Aizone-12.jpg",
rootUrl+"/images/u_work/Aizone-13.jpg"
];
$(images).each(function() {
$('<img/>')[0].src = this;
});
var index = 0;
var i = 1;
$.backstretch(images[index], {
fade: 750,
duration: 4000
});
$('#next').click(function(x) {
x.preventDefault();
$.backstretch(images[i++], {
fade: 750,
duration: 4000
});
$('#output').html(function(i, val) { return val*1+1 });
});
$('#prev').click(function(x) {
x.preventDefault();
$.backstretch(images[i--], {
fade: 750,
duration: 4000
});
$('#output').html(function(i, val) { return val*1-1 });
});
Try this:
http://jsfiddle.net/XejZV/