preface
Recently, I’ve been working on compatibility issues with
Special remind
about<audio>
The compatibility problem of the label mobile terminal can not be solved simply by the front-end debugging, it needs to be synchronized to the back-end commissioning!
First debugging
I’ve done a lot of research on the audio tag, including stackOverflow and other communities (including Nuggets), and even found that the currentTime property of < Audio > in earlier versions of Chrome must be set to a string. More strange knowledge!
First of all, the project is on VUE, so I have built a simple audio component with imperfect functions. As a test purpose, of course, you can also refer to it.
Github address: github.com/Chrischenny…
Of course, it is also available for download on NPM
npm install ch-audio
Copy the code
Okay, let’s get down to business!
The problem: With the custom audio component, the progress bar can’t be dragged, or dragged back to the starting point.
Let’s start with our component drag logic:
data(){
return{
url:' '.stop:true.// Pause the flag
progressX:0.// Progress bar width value, used for cache
offset:0.// Distance between the progress bar and the screen
audioitself:null./ / the cache audio
block:null// Cache the touch area}},methods:{
playAudio(){ / / play
this.stop = !this.stop;
this.$refs.oad.play();
},
stopAudio(){ / / pause
this.stop = !this.stop;
this.$refs.oad.pause();
},
handleTouchstart(e){// Touch the event to start the event
// Remove the timeUpdate event to prevent interference with the progress bar;
this.audioitself.removeEventListener('timeupdate'.this.handleTimeupdate);
this.progressX = e.changedTouches[0].clientX - this.offset;
},
handleTouchmove(e){// Touch the move listening event
if(e.changedTouches[0].clientX - this.offset>this.width){
this.progressX = this.width;
}else if(e.changedTouches[0].clientX - this.offset<0) {this.progressX = 0;
}else{
this.progressX = e.changedTouches[0].clientX - this.offset;
}
},
handleTouchend(e){// Touch ends the listening event
this.audioitself.currentTime = ((e.changedTouches[0].clientX-this.offset)/this.width)*this.audioitself.duration;
// The touch event ends, and add the timeUpdate event again
this.audioitself.addEventListener('timeupdate'.this.handleTimeupdate);
},
handleTimeupdate(){// Play a progress listening event
this.progressX = this.width*(this.audioitself.currentTime/this.audioitself.duration)
},
}
Copy the code
Without further logic, the basic progress bar is set in this way (VUE may be more convenient to set because of two-way binding of data). Let’s talk about the problems encountered. We must be careful when observing the problems, rather than simply suspending the superficial problems: 1. The local commissioning did not find any problems, so the code was thrown to the server, and the mobile test found that the progress bar was not unable to drag, but did not let go. The progress bar could follow the hand. This tells us that the problem is with the Touchend event
Second, the second debugging
The code assumes that the problem is with the currentTime setup, so there is a legacy of chrome. [bug Mc-10887] – A new problem with currentTime debugging is that the progress bar on the browser will return to the original point after being dragged… Strange! (I’m using the page on the server here). I printed currentTime: Normal
There was no problem with local commissioning, but there was a problem with putting the page on the server! So, is the problem on the server side?
After a few rounds of talking, I came up with the answer: we need a response header on the server side:
"Accept-Ranges":"bytes"
Copy the code
Yes, if you set the response header, you can drag the progress bar correctly, so why? Knowing it, not knowing why, that and not knowing the difference! Keep looking!
Let’s first look at what our request header and corresponding header look like without the response header set.
"Accept-Ranges":"bytes"
Accept-ranges:
Let’s take a look at what MDN has to say about this request:
When the browser finds"Accept-Ranges"
When heading, you can try to continue the interrupted download instead of restarting it.
Let’s look at the debug screen:
"Accept-Ranges"
In other words, if we don’t set “Accept-Ranges” correctly, the browser will think that we are going to start downloading the video again, not from the breakpoint, there is no such thing as caching, so the browser will forbid us to set currentTime artificially (although after hitting play, I started to download the video, but the response header has keepalive, so the browser thinks it’s a link, so currentTime still can’t be set. In fact, even if the connection is timed out, as long as your response header doesn’t have “Accept-Ranges”:”bytes” it still won’t let you set it. It looks like IE11 can be set up, there are big guys willing to try, hey hey)
Here are two references, not necessarily the best, but very helpful to me!
www.php.cn/js-tutorial…
www.cnblogs.com/simonbaker/…
Third, the third debugging
With the above conclusion, overjoyed! Was it successful? Start mobile phone debugging, Android success! IOS is still failing!
Why is IOS still failing when it’s about to crash? IOS is the nightmare of mobile compatibility! Continue to debug it, the next debugging all need to be carried out in the mobile phone, very convenient…
Problem code area:
handleTouchend(e){// Touch ends the listening event
this.audioitself.currentTime = ((e.changedTouches[0].clientX-this.offset)/this.width)*this.audioitself.duration;
// The touch event ends, and add the timeUpdate event again
this.audioitself.addEventListener('timeupdate'.this.handleTimeupdate);
},
Copy the code
1, Check currentTime first, error, NaN! Since it’s NaN, there must be a problem with some value in the numerical calculation!
E.touches [0]. Touches[0].
This. offset,this.width are both numbers.
4, in the end, this. Audioitself. Duration, out of the question, the display is infinity!!!!
Why infinity?
The first thing that comes to mind is, how did our
get its own duration? The obvious answer is to get it from the server! Let’s look again at the header returned by the server, this time with the “Accept-ranges “:”bytes” response header.
Content-Length:****
<audio>
“Range”:”bytes=0-“, “bytes=0-“, “I’m going to ask you for files starting from 0bytes, how many to give, depending on your heart.” Well, the server just gave you the data.
IOS Range request header
IOS: Range: bytes=0-1 IOS: Range: bytes=0-1 You’re so petty, you want 1byte? If you return the request header the Android way, they don’t care about you anymore, they don’t ask for bytes, and your duration becomes infinity! See the request header below:
No pun intended, the Content-range header. Let’s look at MDN’s definition of content-range:
In HTTP, the response headerContent-Range
Displays the location of a piece of data in the entire file.
Look at the format:
Content-Range: <unit> <range-start>-<range-end>/<size>
Content-Range: <unit> <range-start>-<range-end>/*
Content-Range: <unit> */<size>
Copy the code
So, if IOS needs to get information about the audio size, it doesn’t recognize the Content-Length header. So the only way to do that is from the content-range… (= =; The first of these three formats has a file size, so it is clear. Therefore, the interface of the back end can not be directly static to access, but to dynamically respond to the requirements of IOS, and return the corresponding response header, can refer to the following code, using the KOA framework:
router.get('/getsource/:filename'.async ctx=>{
var mp3 = path.resolve('./view/source/' + ctx.params.filename);// Obtain the specific path of the server audio resource
ctx.set({
'Content-Type': 'audio/mpeg'.'Content-Length': fs.statSync(mp3).size,// If not statically obtained, these two headers need to be added themselves and must be added
})
if (ctx.headers.range === 'bytes=0-1') {// Check whether a pre-request is initiated by IOS
ctx.set('Content-Range'.`bytes 0-1/${fs.statSync(mp3).size}`) // That's the point
ctx.body = '1'
} else {
ctx.set({
'Accept-Ranges': 'bytes',})const src = fs.createReadStream(mp3)
ctx.body = src
}
});
Copy the code
We’re almost done here! Test it out. You can drag it. Everything works! It’s just that the server is a little slow. Loading takes time… Then, finally, let’s see what IOS will send us after we correctly respond to an IOS request:
As you can see, the second IOS request is still a small number of bytes — just over 16,000 bytes — but the third request takes all 1.5 million bytes at once.
In addition, I also found that every time I re-tested IOS, the number of secondary bytes was the same number, which is about 1% more than the total number of bytes. It may be fixed
conclusion
This time about
compatibility commissioning, harvest is still huge, at the same time let me realize their own shortcomings! Especially for HTTP protocol this piece of content, or need to dig! There are a lot of unknown small details, if you don’t pay attention to, like this time, struggle for a long time ~
Write the last words
Small hit an advertisement, there are big men who need SMS business, or number authentication ability, you can see here! China Unicom Innovation capability platform operator official platform! No middleman to earn the difference ~