react 利用react-hammerjs插件实现滑动特效和点击特效
react-hammerjs是一款由hammer.js的JS插件来实现在react中实现手势滑动的事件插件,
它有各种各样的手势支持效果,这里我们就使用下它最简单的3种效果来实现我们要的动画
分别是点击(onTap)、滑动(onPan)、滑动结束(onPanEnd)
具体事件参数和其它事件可以参考官网
动画效果
上代码
import React from 'react' import {draw} from './canvas' import Hammer from 'react-hammerjs' import axios from './../axios' import "./index.css" export default class Index extends React.Component{ state = { bubble:[], bubbleState:true, bubbleClass:[ { class:'bubble-1', }, { class:'bubble-2', }, { class:'bubble-3', }, { class:'bubble-4', }, { class:'bubble-5', } ], fishList:[], param:[ { version:'v1', cityid:'101020100' } ] } componentWillMount() { this.tem() let num = Math.floor(Math.random()*5); console.log("num----->",num) } tem=()=>{ //天气温度 axios.get("api/",this.state.param, result=> { console.log("--------->",result) this.setState({listData:result.data ||[]}) }, result=> { } ); } handleClick=(e)=>{ //点击 let fishList = this.state.fishList; let leftX = e.center.y let topY = e.center.y let fish = {} if(1080/2>e.center.x){ //左 console.log("点击-1-》",e,topY,1080/2>e.center.x); fish.left = '-30' fish.right = '' fish.top = topY fish.class='fishClassL-1' fish.src='./video/fish-1.jpg' }else { //右 fish.left = '' fish.right = '-30' fish.top = topY fish.class='fishClassR-1' fish.src='./video/fish-2.jpg' console.log("点击-2-》",e,topY,1080/2>e.center.x); } console.log("fish----->",fish) fishList.push(fish) this.setState({ fishList:fishList }) } onPan=(e)=>{ //划动 let bubbleState = this.state.bubbleState if(bubbleState){ let num = Math.floor(Math.random()*5); let bubbleClass = this.state.bubbleClass let bubble = {} bubble.left = e.center.x bubble.top = e.center.y bubble.src = './video/bubble-1.png' bubble.class = bubbleClass[num].class // bubble.class = 'bubble-3' let bubbleList = this.state.bubble bubbleList.push(bubble) // console.log("划动--》",e); this.setState({ bubble:bubbleList, // bubbleState:false }) } } onPanEnd =()=>{ this.setState({ bubbleState:false }) console.log("划动结束--》"); setTimeout(()=> { this.setState({ bubble:[], bubbleState:true }) },3000) } componentDidMount() { if (this.canvas) { draw(this.canvas) } } render() { const { percent,tops } = this.state; return( <Hammer onTap={this.handleClick} onPan={this.onPan} onPanEnd={this.onPanEnd} > <div id={'scrollBox'} className="video-box"> {/*<div className="fish" style={{left:`${percent}%`,top:`${tops}px`}}></div>*/} { this.state.bubble && this.state.bubble.map((item,index)=>{ return ( <div className="bubble-box" key={index} style={{left:`${item.left}px`,top:`${item.top}px`}}><img className={`${item.class}`} src={item.src} /></div> ) }) } { this.state.fishList && this.state.fishList.map((item,index)=>{ return ( <div id="fish-box" className={`${item.class}`} key={index} style={{left:`${item.left}px`,right:`${item.right}px`,top:`${item.top}px`}}><img src={item.src} /></div> ) }) } <canvas width='1080' height='1920' ref={node => this.canvas = node} style={{width: '100%',height: 'auto',}}></canvas> </div> </Hammer> ) } }
.video-box{ /*background-color: #0BB20C;*/ height: auto; width: 100%; position: relative; } .bubble-box{ position: absolute; } .bubble-box img{ } .bubble-box img.bubble-1{ width: 20px; height: 20px; animation:mymove1 5s normal; } .bubble-box img.bubble-2{ width: 10px; height: 10px; animation:mymove2 4s normal; } .bubble-box img.bubble-3{ width: 15px; height: 15px; animation:mymove3 5s normal; } .bubble-box img.bubble-4{ width: 18px; height: 18px; animation:mymove4 4s normal; } .bubble-box img.bubble-5{ width: 12px; height: 12px; animation:mymove5 5s normal; } .fish{ position: absolute; /*left: 0;*/ width: 20px; height: 20px; background-color: #0CD0C0; } @keyframes mymove1 { 0% { opacity: 1; transform:translateY(0px); } 100% { opacity: 0; transform:translateY(-55px); } } @keyframes mymove2 { 0% { opacity: 1; transform:translateY(0px); } 100% { opacity: 0; transform:translateY(-65px); } } @keyframes mymove3 { 0% { opacity: 1; transform:translateY(0px); } 100% { opacity: 0; transform:translateY(-75px); } } @keyframes mymove4 { 0% { opacity: 1; transform:translateY(0px); } 100% { opacity: 0; transform:translateY(-95px); } } @keyframes mymove5 { 0% { opacity: 1; transform:translateY(0px); } 100% { opacity: 0; transform:translateY(-85px); } } #fish-box{ position: absolute; } #fish-box img{ width: 30px; height: 30px; } .fishClassL-1{ animation:fishmovel1 10s normal; } @keyframes fishmovel1 { 0% { left: -30px; } 100% { left: 1100px; } } .fishClassR-1{ animation:fishmover1 10s normal; } @keyframes fishmover1 { 0% { right: -30px; } 100% { right: 1100px; } } /*1920*1080*/
利用canvas将视频渲染成动画背景
export const draw = (canvas) => { if (canvas) { //获取canvas上下文 let ctx = canvas.getContext('2d'); let videoState = true; //创建video标签,并且设置相关属性 let video = document.createElement('video'); let videoSrc1 = './video/video-2.mp4' let videoSrc2 = './video/video-3.mp4' video.preload = true; video.autoplay = true; video.loop = true; video.src = videoSrc1; // video.className = 'aaaaaaaaa' //document.body.appendChild(video); //监听video的play事件,一旦开始,就把video逐帧绘制到canvas上 video.addEventListener('play',() => { let play = () => { ctx.drawImage(video,0,0); requestAnimationFrame(play); }; play(); },false) //暂停/播放视频 canvas.addEventListener('click',() => { // if (!video.paused) { // video.pause(); // console.log("暂停视频!") // } else { // video.play(); // console.log("开始视频!") // } if (videoState) { // video.src = videoSrc2; // videoState = false; // canvasBg = canvas } else { // video.src = videoSrc1; // videoState = true; } },false); } }
封装axios插件
// import JsonP from 'jsonp' import axios from 'axios' // import showMsg from '../components/notification' // 配置API接口地址 var root = 'https://www.tianqiapi.com/' // var root = '/prefix' // 自定义判断元素类型JS function toType (obj) { return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase() } // 参数过滤函数 function filterNull (o) { for (var key in o) { if (o[key] === null) { delete o[key] } if (toType(o[key]) === 'string') { o[key] = o[key].trim() } else if (toType(o[key]) === 'object') { o[key] = filterNull(o[key]) } else if (toType(o[key]) === 'array') { o[key] = filterNull(o[key]) } } return o } /* 接口处理函数 这个函数每个项目都是不一样的,我现在调整的是适用于 https://cnodejs.org/api/v1 的接口,如果是其他接口 需要根据接口的参数进行调整。参考说明文档地址: https://cnodejs.org/topic/5378720ed6e2d16149fa16bd 主要是,不同的接口的成功标识和失败提示是不一致的。 另外,不同的项目的处理方法也是不一致的,这里出错就是简单的alert */ function apiAxios (method, url, params, success, failure) { if (params) { params = filterNull(params) } axios({ method: method, url: url, data: method === 'POST' || method === 'PUT' ? params : null, params: method === 'GET' || method === 'DELETE' ? params : null, baseURL: root, withCredentials: false }).then(function (res) { // if (res.data.success === true) { // if (success) { // success(res.data) // } // } else { // if (failure) { // failure(res.data) // } else { // // window.alert('error: ' + JSON.stringify(res.data)) // console.log('error: 222' + JSON.stringify(res.data)); // } // } success(res.data); // success(JSON.stringify(res.data)); }).catch(function (err) { // let res = err.response // if (err) { // // window.alert('api error, HTTP CODE: ' + res.status) console.log('api error333, HTTP CODE: ' + err.status); // } // showMsg.error('api error, HTTP CODE: ' +err.status); }) } // 返回在react模板中的调用接口 export default { get: function (url, params, success, failure) { return apiAxios('GET', url, params, success, failure) }, post: function (url, params, success, failure) { return apiAxios('POST', url, params, success, failure) }, put: function (url, params, success, failure) { return apiAxios('PUT', url, params, success, failure) }, delete: function (url, params, success, failure) { return apiAxios('DELETE', url, params, success, failure) } }