react页面引导组件, 支持语音播报

9,413 阅读3分钟

页面引导在用户第一次访问网站能过提供很好的提示, 下面介绍基于react写的一个页面引导的组件. 演示地址

效果图

Guide组件的实现

可以把<Guide/>设计成一个容器组件, 因为我们不知道要引导的内容是什么, 通过容器组件的this.props.children渲染内容

class Guide extends Component {
    render () {
        return (
            <div className="guide-container" ref={e => this.guide = e}>
              {this.props.children}
            </div>
        )
    }
}

如何获取哪些是要引导的dom? 可以通过dom的自定义属性, 再通过querySelectorAll获取

// example
<Guide >
    <header data-step="1" data-tip='Welcome to use react-guide'>React Guide</header>
</Guide>
// 获取要引导的dom
this.guide.querySelectorAll('[data-step]')

<Guide/>组件还需要有: 遮罩层、提示框、内容区、语音功能, 4个部分.

遮罩层

遮罩层通过fixed布局,加个透明度就好了, 通过外面传来的visible控制显示

class Guide extends Component {
    render () {
        return (
            <div className="guide-container" ref={e => this.guide = e}>
              {this.props.children}
              {this.props.visible&&<div className="guide-shadow" ref={e => this.shadow = e}s.onClickShadow.bind(this)} key='guide-shadow'></div>}
            </div>
        )
    }
}

提示框

提示框应该再遮罩层之上, 它的z-index大于遮罩层的.提示框还要考虑页面空余空间,确定摆放位置,如下图的4个位置, 1, 4 位置放不下, 所以可以放2, 3.

再添加上resize事件监听, 在页面缩放时,也能重新布局

window.addEventListener('resize', this.onRezieWindow.bind(this), false)

内容区

首先确定要显示内容区的位置, 通过目标domoffsertLeftoffsetTopheightwidth, 获取内容区的位置

const nodeList = getListFromLike(this.guide.querySelectorAll('[data-step]'))  // 获取所有要引导dom
nodeList.sort((a, b) => {
  return Number(a.getAttribute('data-step'))- Number(b.getAttribute('data-step'))
})  // 按照step的大小进行排序
let dots = nodeList.map(node => {
  let height = node.clientHeight || node.offsetHeight
  let width = node.clientWidth || node.offsetWidth
  return {
    left: node.offsetLeft, 
    top: node.offsetTop,
    height,
    width,
    tip: node.getAttribute('data-tip'),
    step: node.getAttribute('data-step'),
    fRight: node.offsetLeft + width,
    fBottom: node.offsetTop + height
  }
})

内容区也在遮罩层之上.激活content时只要给原dom添加上z-index

node.style.setProperty('position', 'relative');
node.style.setProperty('z-index', '999996', 'important');

当页面存在滚动条时, 还要页面的滚动到要引导区域, 通过scrollTo(x, y)实现

window.scrollTo(dLeft - 100, dTop - 100)

语音功能

语音功能可以用HTML5audio标签

<audio ref={e => this.audio = e} src={this.state.audioUrl} type="audio/mpeg"></audio>}

再结合百度的ttsAPI

function text2Voice(tip, lan){
  let obj = {
    lan,
    ie: 'UTF-8',
    spd: '4',
    per: 4,
    text: tip  // tip就是dom上data-tip的属性值
  }
  return 'http://tts.baidu.com/text2audio' + axiosObj(obj)
}

audio标签的src指向text2Voice(tip, lan)的结果

通过audioapi控制停止、播放

this.audio.autoplay = true // 自动播放
this.audio.pause()  // 暂停
this.audio.addEventListener('timeupdate', () => {
    ...  // 监听什么时候结束
}, false)

说明

源码及api➡️github, 欢迎star,感谢.

安装

可以通过npm安装

$ npm install react-guide

API

下面是react-guideapi

Property Description Type Default
visible Whether the guide is visible or not boolean false
audio Whether a voice reads of tip of the guide or not boolean true
lan The voice of language, 'en' or 'zh' string en
bullet Whether bullets (.) button is visible on middle of the guide or not boolean false
num Whether num icon is visible on top left of the guide or not boolean false
onCancel Specify a function that will be called when a user clicks shadow, skip button on bottom left function(e) -
onOk Specify a function that will be called when all steps have done and click the done button function(e) -
data-step Number of steps for guides, only use in dom string -
data-tip Every step you want to show tip, only use in dom string -

例子

一个例子

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Guide from 'react-guide'
class App extends Component {
  constructor () {
    super()
    this.state = {
      visible: false
    }
  }
  handleStart() {
    this.setState({
      visible: true
    })
  }
  handleCancel() {
    this.setState({
      visible: false
    })
  }
  render() {
    return (
      <div>
        <Guide 
          visible={this.state.visible} 
          onCancel={this.handleCancel.bind(this)} >
            <h1 data-step="1" data-tip='Hello World'>Step1</h1>
            <div data-step="3" data-tip='Welcome to use react-guide'>Step3</div>
            <h4 data-step="2" data-tip='react-guide is very easy' >Step2</h4>
            <div><span data-step="4" data-tip='Let start'>Step4</span></div>
      </Guide>
      <button onClick={this.handleStart.bind(this)}>start</button>
    </div>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('root'));