开始
最近做了一个关于视频的嵌入页(嵌入页是分享页的加强版)的需求,主要涉及h5视频组件定制化,评论列表键盘输入遮挡,还有一些js跟客户端交互问题,跟大家交流下公司h5嵌入页的开发流程。
需求
- 实现一个自定义的视频播放器,滑动置顶,难点:(视频定制化,兼容性,事件交互)
- 评论列表页,包括列表展示,评论回复,点赞。难点:(逻辑复杂、客户端交互复杂)
- 底部评论发表功能。难点:(键盘遮挡问题)
效果图:


调试工具
- webview调试技巧:因为我们的页面需要很多客户端环境提供支持(包括参数加签,调用客户端方法等等),所以以往一样在Chrome里调试是行不通的,所以我们要把我们的调试页面嵌入到客户端,但是又想做到跟本地Chrome调试一样可以实时更新代码,实时看到效果,最好还能拥有Chrome一样的控制台。
1.针对第一点可以这样做:在本地起一个服务,比如localhost:3000,找到自己电脑的ip,比如:192.168.5.102,然后把 http://192.168.5.102:3000这个url告诉客户端,让他们配合伪造一个入口(需要的参数从url后获取),这样就可以实现实时更新代码,实时看到效果,跟本地调试效果一样
2.使用手机调试工具,我使用的事腾讯提供的 vconsole,可以实现一个简单的类似Chrome的控制台效果
- 网络代理跟抓包工具:推荐使用charles,可以方便地实现网络代理跟接口的抓包,定位网络的问题
难点一:视频定制化
解决方案:由于video标签兼容性复杂,写法多样,我的做法是使用比较流行的video库,基本可以解决基本的兼容问题,之后要考虑的只有在此基础上自定义controllerBar,利用video库本身就提供的一些事件监听,可以简单有效的实现自定义video。因为开发使用的是react,所以我使用的是star比较多,文档比较全的video-react
难点二:无线列表滚动及常用组件的使用
这些常用的功能已经有很多成熟的组件库为我们做好了,因为用的react,所以用了ant-mobile,有时候会需要自定义主题,参考这个,不重复造轮子 https://mobile.ant.design/docs/react/customize-theme-cn
难点三:键盘问题
键盘问题具体体现在以下几个问题:
- position:fixed,会出现如下问题

- 输入框被键盘遮挡问题:

针对以上问题提出解决方案:
1.用absolute代替fixed
2.scrollIntoView来自动让元素出现在可视区域
思路:用absolute代替fixed,创建一层宽高100%的蒙版,每次唤起键盘的时候记录当前滚动条高度(因为之后要回滚),然后将scrolltop设置为0,滚动到最上方,同时唤出蒙版层,为了保证input能够出现在可视区下边缘,对输入组件用scrollIntoView(false),让其自动出现在键盘上方,如图:
然后我们要阻止触摸滚动,用了生庆哥共享的代码,然后我们在发送评论之后以及点击蒙版之后,回滚到之前的位置,同时隐藏蒙版跟输入框。
禁止触摸滚动代码
export function disableScroll() {
if (window.addEventListener) // older FF
window.addEventListener('DOMMouseScroll', preventDefault, false);
window.onwheel = preventDefault; // modern standard
window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE
window.ontouchmove = preventDefault; // mobile
document.onkeydown = preventDefaultForScrollKeys;
}
export function enableScroll() {
if (window.removeEventListener)
window.removeEventListener('DOMMouseScroll', preventDefault, false);
window.onmousewheel = document.onmousewheel = null;
window.onwheel = null;
window.ontouchmove = null;
document.onkeydown = null;
}
var keys = {37: 1, 38: 1, 39: 1, 40: 1};
function preventDefault(e) {
e = e || window.event;
if (e.preventDefault)
e.preventDefault();
e.returnValue = false;
}
function preventDefaultForScrollKeys(e) {
if (keys[e.keyCode]) {
preventDefault(e);
return false;
}
}
键盘组件代码
export default class Dialog extends React.Component {
constructor() {
super()
const doc = window.document;
this.node = doc.createElement('div');
doc.body.appendChild(this.node);
}
componentDidMount() {
this.textArea.focus()
this.scrollTop = document.body.scrollTop // 记录滚动条位置
document.body.scrollTop = 0
/* 每次唤起键盘都要将页面滚动到最顶端,
这是因为我们用的是position:absolute, top: 0;left:0;right: 0;bottom: 0;
这个组件只出现在页面顶部,这是用绝对定位代替固定定位的关键*/
}
onFocus = (e) => {
setTimeout(() => {
if(this.submit) {
this.submit.scrollIntoView(false)
/* 这里一定要用false,刚好是它滚动到键盘下边缘,
为什么要用setTimeout?因为键盘唤起有延时,特别是某些机型的安卓机 */
}
}, 0);
}
maskClcik = (ifScrollTop) => {
document.body.scrollTop = ifScrollTop ? 0 : this.props.scrollTop // 蒙版点击恢复到之前的滚动条高度
this.props.focusHandle()
}
....
render() {
return createPortal( // 试了一下react v16的传送门,还挺好用的
<div className="dialog" onClick={()=>this.maskClcik(false)}>
<div className="share-bottom-fixed" ref={(dom)=>this.submit=dom}>
<TextareaItem
onFocus={(e)=>this.onFocus(e)}
// onBlur={this.maskClcik}
ref={(dom)=>this.textArea=dom}
onChange={this.onChange}
autoHeight={true}
value={this.props.commentContent}
className="write-comment"
placeholder={this.props.reviewType === 3 ? '回复'+this.props.reviewNickName:'写评论'} />
<span className='submit' onClick={(e)=>this.submitHandle(e)}>发送</span>
</div>
</div>, //塞进传送门的JSX
this.node //传送门的另一端DOM node
);
}
componentWillUnmount() {
window.document.body.removeChild(this.node);
}
}
总结:
1.用绝对定位跟滚动条滚动到顶部的方法模仿固定定位
2.利用scrollIntoView(新属性,移动端兼容性良好) 使其每次都能自动出现在键盘上方
下面是ios跟安卓的实现效果图
难点四:客户端交互问题
1.评论区存在很多与客户端交互的需求(包括跳转小家、跳转评论列表、举报等等)
- 接口加签参数获取
- 分享
原理:
1.js调用客户端方法:客户端会把交给js调用的方法挂载在js的window对象上,调用的时候 window.fn()
客户端调用js方法:跟客户端预定好方法,然后再js的window对象上定义方法:如下:
window.continuePlay = function() {
const player = document.getElementsByTagName('video')[0]
return player?player.play():null
}
- 使用库 bbtNative:原理同上,提供了小时光跟孕育的一些公用方法,大家自行看文档。有一下使用注意事项:
1.客户端方法会在页面加载完之后才会把方法挂载在webview上,这个时间会根据不同机型而不同,iOS快,Android慢,所以马上获取数据的话,需要一个定时器,否则无法获取加签参数
componentDidMount() {
Toast.loading('', 12)
setTimeout(()=>{
this.fetchData()
this.fetchDetail()
}, 1000)
}
2.其中遇到一个比较坑的问题,bbtNative.encryptApi,这个加签函数安卓机无法返回加签值,后面去看了下bbtNative库的源码,发现了一些问题,改了源码之后已经可以获取加签值,但目前生庆哥还没验证,所以gitlab上的库还是存在问题,要是大家以后使用遇到类似问题可以与我讨论
收获
做了这个项目还是收获颇多的
- 对公司项目的开发流程更加清晰明确
- 对h5于在webview中于原生语言交互有了大概的了解
- 创新了一种h5键盘遮挡的有效方案
- 能够认识ios开发跟Android开发程序员(为之后我要入手公司的rn项目打下基础)
