I try to pause a CSS animation with JavaScript.
This works in all tested browsers but not in Safari on iOS, where the animation continues until the end.
How to fix or workaround this bug?
(function () {
function setAnimationPlayState (state) {
document.getElementById ('debug').innerHTML += state + '/';
document.getElementById ('animation').style.animationPlayState = state;
document.getElementById ('animation').style.webkitAnimationPlayState = state;
}
window.addEventListener ('load', function () {
setAnimationPlayState ('running');
setTimeout (function () {
setAnimationPlayState ('paused');
}, 1000);
});
} ());
#animation {
animation: test linear 4s;
animation-fill-mode: both;
}
#keyframes test {
to {
/* 3D never pause */
transform: rotateY(90deg);
/* 2D may pause or not */
/* transform: translateX(100vw); */
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="debug" style="color: red;">/</div>
<div id="animation">Hello<br>this text<br>should not<br>disappear<br>completely</div>
</body>
</html>
My animation is 3D so I also tested with a 2D animation just in case, and the behavior is even weirder. Sometimes it works, then I reload the page and it doesn't work. This thing is driving me crazy, your help is most welcome.
Related
I have an application with multiple stack of card, I would like to have a drag&drop ability between these stacks.
These stacks have a different layout in term of offset relative to the parent element.
I simplified to the max in this fiddle
https://jsfiddle.net/dtghbo7f/
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Crapette HTML 5 + jQuery</title>
<style type="text/css">
.card, .box {
width: 71px;
height: 96px;
position: absolute;
}
#box1 {
top:31px;
left:25px;
}
#box2 {
top:31px;
left:255px;
}
.card {
background-image: url[...]
}
.box {
background-position: -1px -1px;
background-image: url[...]
}
</style>
<script src="jquery.min.js"></script>
<script src="jquery-ui.js"></script>
<script>
$("body").ready(function() {
$('<div>').attr('id','card0').addClass('card').appendTo($('#box1'));
for(let i=1;i<7;i++){
$('<div>').attr('id','card'+i).addClass('card').appendTo($('#box1 div.card:not(:has(*))')).css('top',5).css('left',5);
}
$('<div>').attr('id','card7').addClass('card').appendTo($('#box2'));
for(let i=1;i<7;i++){
$('<div>').attr('id','card'+(i+7)).addClass('card').appendTo($('#box2 div.card:not(:has(*))')).css('top',15);
}
$(".card").draggable({
revert: true,//'invalid',
revertDuration: 500,
start: function(event, ui) {
$(this).parents(".box").css('z-index',2);
},
drag: function(event, ui) {
},
stop: function(event, ui) {
$(".box").css('z-index',1);
if($(this).parents('.box').attr('id') == 'box1') {
$(this).css('top',5).css('left',5);
} else {
$(this).css('top',15).css('left',0);
}
},
});
$(".box, .card").droppable({
activate: function( event, ui ) {return false;},
drop: function( event, ui ) {
let source_offset = ui.draggable.parent().offset();
let destination_offset = $(this).offset();
$(this)
.append(ui.draggable);
ui.draggable
.css('top', parseInt(ui.draggable.css('top')) + parseInt(source_offset.top) - parseInt(destination_offset.top))
.css('left', parseInt(ui.draggable.css('left')) + parseInt(source_offset.left) - parseInt(destination_offset.left));
console.log(ui.draggable.css('top'), ui.draggable.css('left'));
$('.card, .box').droppable('enable');
$('.card:has(*), .box:has(*)').droppable('disable');
},
});
$('.card, .box').droppable('enable');
$('.card:has(*), .box:has(*)').droppable('disable');
});
</script>
</head>
<body>
<div id='box1' class='box'>
</div>
<div id='box2' class='box'>
</div>
</body>
</html>
As you can see, when you drop a card on the other stack, the revert options move the card to the original position relative to the parent. As this offset change on different stack, I would like to be able to modify this originalPosition when the droppable stack is determined. Can you help me ?
In my app(audio base app), i'm playing remote audio.
before start playing i set following category
AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
so while i'm playing audio, i also play vimeo video in WKWebView which cause audio interrupt. how to avoid this behaviour?
<!DOCTYPE html>
<html>
<body>
<style type="text/css">
body, html {width: 100%; height: 100%; margin: 0; padding: 0;background:black}
.main {position: absolute;top: 0; left: 0; right: 0; background-color: #101010;height:100%;}
.main iframe {display: block; width: 100%; height: 100%; border: none;}
</style>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<script src="https://player.vimeo.com/api/player.js"></script>
<div class="main">
<iframe src=\(url)></iframe>
</div>
<script src="https://player.vimeo.com/api/player.js"></script>
<script>
var iframe = document.querySelector('iframe');
var player = new Vimeo.Player(iframe);
player.on('play', function() {
window.webkit.messageHandlers.play.postMessage("play")
});
player.on('pause', function() {
window.webkit.messageHandlers.pause.postMessage("pause")
});
player.on('ended', function(data) {
window.webkit.messageHandlers.ended.postMessage("ended")
});
player.on('bufferstart', function(data) {
window.webkit.messageHandlers.bufferstart.postMessage("bufferstart")
});
player.on('bufferend', function(data) {
window.webkit.messageHandlers.bufferend.postMessage("bufferend")
});
Promise.all([player.getVideoWidth(), player.getVideoHeight()]).then(function(dimensions) {
window.webkit.messageHandlers.dimensions.postMessage(dimensions)
});
player.getDuration().then(function(duration) {
window.webkit.messageHandlers.duration.postMessage(duration);
});
player.ready().then(function () {
player.setVolume(1)
window.webkit.messageHandlers.ready.postMessage("ready");
});
function play() {
player.play();
}
function pause() {
player.pause();
}
function destroy() {
player.destroy();
}
</script>
</body>
</html>
The interruption happens because WKWebview sound is handled by different process and a separate AVAudioSession. The WKWebview AVAudioSessionCategory category will switch to .playback while the sounds is playing either through <audio> or <video> tags (or its javascript counterparts). This must be the underlying mechanism that your vimeo playback uses (otherwise the interrupts would not occur).
There are 2 main routes you can go:
1) If you're ok with your main app sounds(not the WKWebView ones) mixing with any other iOS sounds (like the iPod music playback) then go with #Rog 's suggestion from the comment to use .ambient for your main app audiosession category. (This will also cause your app main audio session not to emit sound while the hardware silent switch is in off position).
2) A more flexible but requiring careful timing handling is to additionally set .mixWithOthers options for your main app audiosession .playback category. You would remove .mixWithOthers once you detect WKWebView is done playing it's own sound. From user perspective it would appear as your app is using .playback all the time.
I covered somewhat related topic in my answer here
I use Phaser to create a game but I found an problem for using it in for example facebook. When an redirect is done within an iframe the canvas is not responding after a click.
Example:
I have a IFrame and within the iframe I redirect to the game.html. When I click in the game.html everything freezes.
Everything works fine when using a computer (any browser), windows phone or android, but with an iphone or ipad it won't work.
Below are the example files to replay the problem...
index.html:
<html>
<body>
<iframe src="click.html" height="900" width="800"/>
</body>
</html>
click.html
<!DOCTYPE html>
<html>
<body>
CLICK HERE
</body>
</html>
Game.html
<!DOCTYPE html>
<html>
<head>
<style>
body{overflow:hidden;}
#game_div {
width: 760px;
height: 1100px;
margin: auto;
}
</style>
<script type="text/javascript" src="./Game/phaser.min.js"></script>
<script type="text/javascript" src="./Game/main.js"></script>
</head>
<body>
<div id="game_div"> </div>
</body>
</html>
main.js
var game = new Phaser.Game(760, 1100, Phaser.AUTO, 'game_div');
var overlay, countdownText;
var counter = 0;
var main_state = {
preload: function() {
},
create: function () {
//game overlay
overlay = game.add.graphics(0, 0);
overlay.beginFill(0x00A54F, 0.8);
overlay.drawRect(0, 0, game.width, game.height);
countdownText = game.add.text((game.width / 2), (game.height / 2), counter, { font: "65px Arial", fill: "#ffffff", align: "center" });
countdownText.anchor.set(0.5,0.5);
},
update: function() {
countdownText.setText(counter++);
}
}
game.state.add('main', main_state);
game.state.start('main');
TNX
Found the solution, with thanks to Rich Davey.
When I add the following code it works:
game.stage.disableVisibilityChange = true;
I've implemented the audio record plugin on IOS using phonegap/cordova 1.7. IOS5.1
I'm using it over the standard media.startrecord() function as I want to change the bitrate to reduce the size of the file.
It works fine in the simulator.
On the real iphone it records successfully once but subsequently refuses.. it seems to work but on playback I'm getting an error 4 MediaError.MEDIA_ERR_NONE_SUPPORTED
I've created a new project with just a bare bones record and playback and I'm still getting the same issue.
Here's the test code - It's very basic - just a couple of links to record two separate files and play them back. The setTimeout is there to record just a couple of seconds of audio each time.
I've googled til I've worn out my fingerprints but haven't found a resolution.
Any insight you can give would be very gratefully received.
I'm wondering if I'm failing to close out the recording/playback properly? Or are files referenced differently on the sim v the iPhone?
Many thanks!
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />
<meta charset="utf-8">
<link rel="stylesheet" media="only screen and (max-device-width: 480px)" href="iphone.css" type="text/css" />
<script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>
<script type="text/javascript">
var fs,mediaRec=null, recInterval,recordSettings = {"FormatID": "kAudioFormatULaw","SampleRate": 16000.0,"NumberOfChannels": 1,"LinearPCMBitDepth": 16};
function recfile1() {recordAudio("test.wav");}
function recfile2() {recordAudio("test2.wav");}
function success(){console.log("ok");}
function recordAudio(fname) {
fs.root.getFile(fname, {create: true, exclusive: false}, function(entry){
mediaRec = new Media(entry.fullPath, success, function(){alert("failed");});
mediaRec.startRecordWithSettings(recordSettings);
recInterval = setTimeout(function() {
mediaRec.stopRecordWithSettings();
}, 2000);
}, function(){console.log("error");});
}
function playfile(fname) {
var my_media;
fs.root.getFile(fname, {create: false, exclusive: false},
function success(entry) {
my_media = new Media(entry.fullPath,function(){console.log("ok");},function(err){alert(err.code+" "+err.message);});
my_media.play();
},
function() {
console.log("not found file");
}
);
}
function onBodyLoad() {
document.addEventListener("deviceready", onDeviceReady, false);
}
function onDeviceReady(){
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem){fs=fileSystem;}, function(){console.log("failed");});
Media.prototype.startRecordWithSettings = function(options) {
Cordova.exec(null, null, "AudioRecord","startAudioRecord", [this.id, this.src, options]);
};
Media.prototype.stopRecordWithSettings = function() {
Cordova.exec(null, null, "AudioRecord","stopAudioRecord", [this.id, this.src]);
};
}
</script>
</head>
<body onload="onBodyLoad()">
<a onclick="playfile('test.wav');">play</a>
<a style='margin-top:100px;' onclick="recfile1();">record 1</a>
<a style='margin-top:100px;' onclick="recfile2();">record 2</a>
<a onclick="playfile('test2.wav');">play2</a>
</body>
</html>
If you build a simple dragger:
$(document).ready(function(){
$('#tomove').draggable({
axis: 'x',
drag: function(event, ui) {
mouseUp();
}
});
});
And you try to stop it programmatically:
function mouseUp() {
if($('#tomove').offset().left > 400) {
$('#tomove').trigger('mouseup');
}
}
You will get this message in error console:
this.helper is null
Is there any way to fix this?
Thanks for your help.
It looks like you're just trying to constrain movement on the draggable element, is this correct? Have you seen this page: http://jqueryui.com/demos/draggable/#constrain-movement
EDIT
How about this instead then: (sample page, does what you're asking - be mindful of the jquery inclusion location)
Also notice I changed the name of the method to be something a little more apropos. This will not stop the user from being able to drag back to the left. I didn't think you wanted to actually stop them if they hit 400 (or whatever other wall).
If you want to do that, you merely $('#element').draggable('destroy')
<html>
<head>
<title>Draggable jQuery-UI Width Block</title>
<script src="jquery-1.4.2.min.js" ></script>
<script src="jquery-ui-1.8.1.custom.min.js" ></script>
</head>
<body>
<div id="tomove" style="width:100px;height:20px;background:silver;">
<span>some text</span>
</div>
<script>
$(document).ready( function() {
$('#tomove').draggable({
axis: 'x',
drag: function(event, ui) {
dragBlock( ui );
}
});
});
function dragBlock( ui ) {
if( ui.position.left > 400 ) {
ui.position.left = '400px';
}
if( ui.position.left < 0 ) {
ui.position.left = '0px';
}
}
</script>
</body>
</html>