i´m testing a Windows 8 app for a touch pannel.
for now i just want to handle the "tapped" events over the screen.
I´ve tried it with a "MSGesture" object, but the tap events fires the 60%-80% of times. It detects all the "pointerdown" events, but not all of them also trigger a tap event.
Now i´ve tried the same with a "GestureRecognizer" object and the problem remains the same: all the "pointerdown" events fire ok, but not all of them are recognize as tap events.
The Code:
gr = new Windows.UI.Input.GestureRecognizer();
gr.gestureSettings = Windows.UI.Input.GestureSettings.tap | Windows.UI.Input.GestureSettings.doubleTap;
gr.addEventListener("tapped", onTap);
var mainGrid = document.getElementById("mainGrid");
mainGrid.addEventListener("pointerdown", onPointerDown, false);
mainGrid.addEventListener('pointermove', processMove, false);
mainGrid.addEventListener('pointerup', processUp, false);
mainGrid.addEventListener('pointercancel', processUp, false);
function onPointerDown(e) {
count++;
counter.innerHTML = "Count: " + count;
// Get the current PointerPoint
var pp = e.getCurrentPoint(e.currentTarget);
// Feed the PointerPoint to GestureRecognizer
gr.processDownEvent(pp);
}
function processMove(e) {
// Get the current PointerPoint
var pps = e.getIntermediatePoints(e.currentTarget);
// Feed the PointerPoint to GestureRecognizer
gr.processMoveEvents(pps);
}
function processUp(e) {
// Get the current PointerPoint
var pp = e.getCurrentPoint(e.currentTarget);
// Feed the PointerPoint to GestureRecognizer
gr.processUpEvent(pp);
}
function onTap(e) {
count2++;
counter2.innerHTML = "Count2: " + count2;
}
As you can see, there are "counter" and "counter2" DOM elements, those are just used for showing a counter that increments each time each event is fired.
Could it be a Windows 8 App performance bug?
Thanks for your help!
Related
Apologies in advance for any terminology mistakes, I'm a student and trying my hardest to be as clear as possible! and thanks in advance for any help!
I'm trying to use Azure Speech-To-Text services. I'd like the user to be able to press a start and stop button to record themselves and print out the transcription. My app will eventually be a React Frontend and Rails backend, but right now I am just trying to understand and work through the demo.
I'm confused by the documentation but was able to get things half working. However, right now it just continuously listens to the speaker and never stops.
I want to use stopContinuousRecognitionAsync() or recognizer.close() once a button is pressed, but I cannot seem to get it working. The farthest I've gotten is the result is logged only once the stop button is pressed, but it continues to listen and print out results. I've also tried using recognizer.close() -> recognizer = undefined but to no avail. I am guessing that due to the asynchronous behavior, it closes out the recognizer before logging a result.
The latest code I've tried is below. This result starts listening on start click and prints speech on stop, but continues to listen and log results.
// subscription key and region for speech services.
var subscriptionKey, serviceRegion;
var authorizationToken;
var SpeechSDK;
var recognizer;
document.addEventListener("DOMContentLoaded", function () {
startRecognizeOnceAsyncButton = document.getElementById("startRecognizeOnceAsyncButton");
subscriptionKey = document.getElementById("subscriptionKey");
serviceRegion = document.getElementById("serviceRegion");
phraseDiv = document.getElementById("phraseDiv");
startRecognizeOnceAsyncButton.addEventListener("click", function () {
startRecognizeOnceAsyncButton.disabled = true;
phraseDiv.innerHTML = "";
// if we got an authorization token, use the token. Otherwise use the provided subscription key
var speechConfig;
if (authorizationToken) {
speechConfig = SpeechSDK.SpeechConfig.fromAuthorizationToken(authorizationToken, serviceRegion.value);
} else {
speechConfig = SpeechSDK.SpeechConfig.fromSubscription(“API_KEY”, serviceRegion.value);
}
speechConfig.speechRecognitionLanguage = "en-US";
var audioConfig = SpeechSDK.AudioConfig.fromDefaultMicrophoneInput();
recognizer = new SpeechSDK.SpeechRecognizer(speechConfig, audioConfig);
recognizer.startContinuousRecognitionAsync(function () {}, function (err) {
console.trace("err - " + err);});
stopButton = document.querySelector(".stopButton")
stopButton.addEventListener("click", () =>{
console.log("clicked")
recognizer.recognized = function(s,e) {
console.log("recognized text", e.result.text)
}
})
});
Assuming the recognizer is conjured correctly outside of the code, there's a few things to change to get the result you want.
The events should be hooked to the recognizer before calling startContinuousRecognition().
In the stop button handler, call stop. I'd also hook the stop event outside of the start button click handler.
Quick typed changes, didn't compile. :-)
startRecognizeOnceAsyncButton.addEventListener("click", function () {
startRecognizeOnceAsyncButton.disabled = true;
//div where text is being shown
phraseDiv.innerHTML = "";
// The event recognized signals that a final recognition result is received.
recognizer.recognized = function(s,e) {
console.log("recognized text", e.result.text)
}
//start listening to speaker
recognizer.startContinuousRecognitionAsync(function () {}, function (err) {
console.trace("err - " + err);});
});
stopButton = document.querySelector(".stopButton")
stopButton.addEventListener("click", () =>{
console.log("clicked");
recognizer.stopContinuousRecongition();
};
I have a button on my web application, which has the following code in the click event handler:
const fileInputEl = document.createElement('input');
fileInputEl.type = 'file';
fileInputEl.accept = 'image/*';
fileInputEl.addEventListener('input', (e) => {
if (!e.target.files.length) {
return;
}
// Handle files here...
});
fileInputEl.dispatchEvent(new MouseEvent('click'));
Sometimes (about 1 out of 8), after selecting the file, the input event doesn't fire after choosing a file. I'm guessing this is a browser bug around the lifecycle of the element.
Any way around this short of appending the element to the page and removing it later? What's the proper way to handle this in modern browsers these days?
I'm testing with Google Chrome on Windows.
JSFiddle: http://jsfiddle.net/pja1d5om/2/
Citate from your question: Sometimes (about 1 out of 8), after selecting the file, the input event doesn't fire after choosing a file.
I can confirm this behavior with input and with change events using Opera (ver. 55.0.2994.61, newest version at this time) which uses Google Chrome browser engine "Blink". It happens about 1 out of 25.
Solution
This happens because sometimes your input element object was deleted after file dialog closing because it is not in using anymore. And when it happens you have not the target which could receive input or change event.
To solve this just add your input element somewhere to the DOM after creating as hidden object like follows:
fileInputEl.style.display = 'none';
document.body.appendChild(fileInputEl);
And then when the event was fired you can delete it like follows:
document.body.removeChild(fileInputEl);
Full example
function selectFile()
{
var fileInputEl = document.createElement('input');
fileInputEl.type = 'file';
fileInputEl.accept = 'image/*';
//on this way you can see how many files you select (is for test only):
fileInputEl.multiple = 'multiple';
fileInputEl.style.display = 'none';
document.body.appendChild(fileInputEl);
fileInputEl.addEventListener('input', function(e)
{
// Handle files here...
console.log('You have selected ' + fileInputEl.files.length + ' file(s).');
document.body.removeChild(fileInputEl);
});
try
{
fileInputEl.dispatchEvent(new MouseEvent('click'));
}
catch(e)
{
console.log('Mouse Event error:\n' + e.message);
// TODO:
//Creating and firing synthetic events in IE/MS Edge:
//https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/dn905219(v=vs.85)
}
}
<input type="button" onclick="selectFile()" value="Select file">
Citate from your bounty description: Bounty will be awarded to someone who ... show an appropriate workaround.
My old suggested workaround (now irrelevant)
We can use setInterval function to check if input value was changed. We save intervalID in our new fileInputEl as property. Because we always create a new file input element then its value is always empty on start (on each button click). And if this value was changed we can detect it when we compare it with empty string. And when it happens then we pass our fileInputEl to fileInputChanged() function and clear/stop our interval function.
function selectFile()
{
var fileInputEl = document.createElement('input');
fileInputEl.type = 'file';
fileInputEl.accept = 'image/*';
//on this way you can see how many files you select (is for test only):
fileInputEl.multiple = 'multiple';
fileInputEl.intervalID = setInterval(function()
{
// because we always create a new file input element then
// its value is always empty, but if not then it was changed:
if(fileInputEl.value != '')
fileInputChanged(fileInputEl);
}, 100);
try
{
fileInputEl.dispatchEvent(new MouseEvent('click'));
}
catch(e)
{
console.log('Mouse Event error:\n' + e.message);
// TODO:
//Creating and firing synthetic events in IE/MS Edge:
//https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/dn905219(v=vs.85)
}
}
function fileInputChanged(obj)
{
// Handle files here...
console.log('You have selected ' + obj.files.length + ' file(s).');
clearInterval(obj.intervalID);
}
<input type="button" onclick="selectFile()" value="Select file">
It seems this is a browser bug/fluke and likely has something to do with garbage collection. I can get around it by adding the file input to the document:
fileInputEl.style.display = 'none';
document.querySelector('body').appendChild(fileInputEl);
When done, it can be cleaned up with:
fileInputEl.remove();
I am building a javascript game, and i want to create a background music based on sound file snippets. Short mp3 files to play them as one continuous track. I have tried binding an "ended" event handler on the audio file, though this causes a delay between audio fragments.
To solve this I made a hacky solution that still does not work, changing the audio 1 second before it finishes.
Ebuc.manageAudio = function(){
var listener = function (event) {
if (this.currentTime > (this.duration - 1) && Ebuc.bgnext) {
Ebuc.manageAudio();
console.log("aduio");
Ebuc.bgnext = false;
}
if(this.currentTime < 2){
Ebuc.bgnext = true;
console.log("reset");
}
console.log(event);
console.log("listener active")
};
var color = Level.current.color;
if(Ebuc.bgsong == null) {
Ebuc.bgsong = new Audio('assets/sound/' + Resources.audioSetList[color].getcurrentsong());
Ebuc.bgsong.addEventListener('timeupdate', listener, true);
}
else{
Ebuc.bgsong = new Audio('assets/sound/' + Resources.audioSetList[color].getcurrentsong());
}
Ebuc.bgsong.play();
Resources.audioSetList[color].next();
};
This sample works once, when it is time to switch fragment 2 to fragment 3 the loop stops. Console logging the event listener gives 4 times a log before stopping.
Q1: Why is this eventlistener suddenly disappearing?
Q2: Is there a non hack solution for chaining these audio fragments.
I thank you in advance.
You're going to have more than just pausing issues trying to rapidly switch between two short audio clips, you're going to probably want to crossfade between the two audio tracks quickly as well to prevent any popping, artifacts, etc.
Here's an example of crossfading using howler from howler's github issues. You could probably use this example, and keep a queue of loaded instances to transition to. I hope that helps.
//you'll probably want this crossfade duration to be shorter.
var crossfadeDuration = 5000,
volume = 0.7;
var instance1, instance2, soundDuration;
// Singleton helper to build similar instances
var createHowlerInstance = function (urls, onload) {
return new Howl({
urls: urls,
loop: false,
volume: 0,
onload: onload
});
};
// Create "slave" instance. This instance is meant
// to be played after the first one is done.
instance2 = createHowlerInstance(['file2.mp3']);
// Create "master" instance. The onload function passed to
// the singleton creator will coordinate the crossfaded loop
instance1 = createHowlerInstance(['file1.mp3'], function(){
// Get the sound duration in ms from the Howler engine
soundDuration = Math.floor(instance1._duration * 1000);
(function crossfadedLoop(enteringInstance, leavingInstance){
// Fade in entering instance
enteringInstance.pos(0).play().fade(0, volume, crossfadeDuration);
// Wait for the audio end to fade out entering instance
// white fading in leaving instance
setTimeout(function(){
enteringInstance.fade(volume, 0, crossfadeDuration);
crossfadedLoop(leavingInstance, enteringInstance);
}, soundDuration - crossfadeDuration);
})(instance1, instance2);
});
By using the idea of setting a timeOut in the answer of pantalohnes I have created the following code to solve the gap:
Ebuc.manageAudio = function(){
var color = Level.current.color;
Ebuc.bgsong = new Audio('assets/sound/' + Resources.audioSetList[color].getcurrentsong());
Ebuc.bgsong.addEventListener("loadedmetadata",function(){
setTimeout(Ebuc.manageAudio, (Ebuc.bgsong.duration * 1000) - 50);
Ebuc.bgsong.play();
console.log(Ebuc.bgsong.duration);
Resources.audioSetList[color].next();
});
};
The 50 milliseconds timeout bridges the gap between the sequenced files exactly.
Answering your question (although I see you found another solution), I think I found your bug:
The second time your enter Ebuc.manageAudio(), Ebuc.bgsong is already set and you just create a new Audio Ebuc.bgsong = new Audio(...) without attaching the listener to it, so you're not being notified for any 'timeupdate' events emitted when playing the second audio file.
You should also remove the listener from the previous playing audio.
So, if all else is ok, I think this should fix it:
Ebuc.manageAudio = function(){
var listener = function (event) {
if (this.currentTime > (this.duration - 1) && Ebuc.bgnext) {
Ebuc.manageAudio();
console.log("aduio");
Ebuc.bgnext = false;
}
if(this.currentTime < 2){
Ebuc.bgnext = true;
console.log("reset");
}
console.log(event);
console.log("listener active")
};
var color = Level.current.color;
if(Ebuc.bgsong != null) {
Ebuc.bgsong.removeEventListener('timeupdate', listener, true);
}
Ebuc.bgsong = new Audio('assets/sound/' + Resources.audioSetList[color].getcurrentsong());
Ebuc.bgsong.addEventListener('timeupdate', listener, true);
Ebuc.bgsong.play();
Resources.audioSetList[color].next();
};
More than that, I think that if you properly remove the listener from the previous playing audio, you won't need that bgnext hack at all:
var listener = function (event) {
if (this.currentTime > (this.duration - 1)) {
Ebuc.manageAudio();
console.log("aduio");
}
console.log(event);
console.log("listener active")
};
Ebuc.manageAudio = function () {
var color = Level.current.color;
if (Ebuc.bgsong != null) {
Ebuc.bgsong.removeEventListener('timeupdate', listener, true);
}
Ebuc.bgsong = new Audio('assets/sound/' + Resources.audioSetList[color].getcurrentsong());
Ebuc.bgsong.addEventListener('timeupdate', listener, true);
Ebuc.bgsong.play();
Resources.audioSetList[color].next();
};
Let me know if that worked :)
JavaScript touch events contain properties for radius and force. Unfortunately it appears that events aren't generated when either property changes. Events are only triggered for things like touch start, move or end. Can anyone think of a way to get more updates on change of touch size?
Currently to get radius updates I have to wiggle my finger to trigger the touch move event, but I would prefer a software solution.
I had the same issue and then discovered this blog post: http://blog.framerjs.com/posts/prototyping-3D-touch-interactions.html
In a nutshell, we need to use touchstart event to capture the touch event, assign the event to a variable and then use setInterval to get the force value:
var el = document.getElementById('myElement');
var currTouch = null;
var currTouchInterval = null;
attachListeners = function () {
el.addEventListener('touchstart', enterForceTouch, false);
el.addEventListener('touchend', exitForceTouch, false);
}
enterForceTouch = function (evt) {
evt.preventDefault();
currTouch = evt;
currTouchInterval = setInterval(updateForceTouch, 10); // 100 times per second.
}
updateForceTouch = function() {
if (currTouch) {
console.log(currTouch.touches[0].force); // Log our current force value.
}
}
exitForceTouch = function (evt) {
evt.preventDefault();
currTouch = null;
clearInterval(currTouchInterval);
}
attachListeners();
There is a good example for doing long press in Javascript here: Long Press in JavaScript?
But it does not provide for knowing the duration of the press.
If I want to do different things based on the length of the press I cant use the pattern in that post.
I was trying to do something similar by saving current time in a variable on('mousedown')
and then calculating the time difference on('mouseup').
this works fine within a normal Javasript page in a "normal" browser.
However within my phonegap app something happens,
looks like the mouseup event is not being called if the finger is kept on the screen for a long duration (say 5 sec..).
Is this some native mobile browser behavior? Can I override it somehow?
I am using plain jQuery not jQuery mobile.
Any ideas anyone?
You could have a look at how the taphold and vmouseup (handleTouchEnd() line 752) events are implemented in jQuery mobile source code.
Since it is already tested and implemented I'd suggest to use jquery mobile instead of jquery and modify (since it already handles all the 'quirks' related each mobile browser), and change the code as you need.
You can check the time to identify Click or Long Press [jQuery]
function AddButtonEventListener() {
try {
var mousedowntime;
var presstime;
$("button[id$='" + buttonID + "']").mousedown(function() {
var d = new Date();
mousedowntime = d.getTime();
});
$("button[id$='" + buttonID + "']").mouseup(function() {
var d = new Date();
presstime = d.getTime() - mousedowntime;
if (presstime > 999/*You can decide the time*/) {
//Do_Action_Long_Press_Event();
}
else {
//Do_Action_Click_Event();
}
});
}
catch (err) {
alert(err.message);
}
}
Note that this solution is usefull if you do not use jQuery Mobile for some reason.
I used the article Fast Touch Event Handling and just added a piece of code
$.event.special.tap = {
distanceThreshold: 10,
timeThreshold: 350,
setup: function () {
var self = this,
$self = $(self);
// Bind touch start
$self.on('touchstart', function (startEvent) {
// Save the target element of the start event
var target = startEvent.target,
touchStart = startEvent.originalEvent.touches[0],
startX = touchStart.pageX,
startY = touchStart.pageY,
threshold = $.event.special.tap.distanceThreshold,
timeout,
expired = false;
function timerFired() {
expired = true;
}
function removeTapHandler() {
clearTimeout(timeout);
$self.off('touchmove', moveHandler).off('touchend', tapHandler).off('touchcancel', removeTapHandler);
};
function tapHandler(endEvent) {
removeTapHandler();
if (target == endEvent.target) {
if (expired) {
$.event.simulate('longtap', self, endEvent);
} else {
$.event.simulate('tap', self, endEvent);
}
}
};
// Remove tap and move handlers if the touch moves too far
function moveHandler(moveEvent) {
var touchMove = moveEvent.originalEvent.touches[0],
moveX = touchMove.pageX,
moveY = touchMove.pageY;
if (Math.abs(moveX - startX) > threshold || Math.abs(moveY - startY) > threshold) {
removeTapHandler();
}
};
// Remove the tap and move handlers if the timeout expires
timeout = setTimeout(timerFired, $.event.special.tap.timeThreshold);
// When a touch starts, bind a touch end and touch move handler
$self.on('touchmove', moveHandler).on('touchend', tapHandler).on('touchcancel', removeTapHandler);
});
}
};
So, now I have a tap and a longtap events