• 暂停处理游戏业务逻辑是与React组件联系比较紧的。 暂停处理的React组件如下所示: import React from 'react'; import './Pause.scss'; import * as MiniGame from '../../miniGame'; //only div const s={ ...

    暂停处理

    游戏业务逻辑是与React组件联系比较紧的。
    暂停处理的React组件如下所示:
    这里写图片描述

    import React from 'react';
    import './Pause.scss';
    import * as MiniGame from '../../miniGame';
    //only div
    const s={
        toast:'pauseToast',
        toastOrigin:'toast',
        pause:'pause',
        pauseButton:'pauseButton'
    }
    let stylePause={
        borderLeft:'10px solid white',
        borderRight:'10px solid white',
        display:'inline',
        position:'absolute',
        left:'6%',
        top:'12%',
        width:'auto',
        height:'auto',
        color:'transparent',
        cursor:'pointer',
        zIndex:'1000'
    }
    
    class Pause extends React.Component{
        constructor(props){
            super(props);
            this.state={
                value:'',
                display:'none'
            };
            this.handleClick=this.handleClick.bind(this);
    
        }
    
        componentWillMount(){
    
        }
    
        componentDidMount(){
            //window.onblur=MiniGame.windowOnBlur(this);
            //window.onfocus=MiniGame.windowOnFocus(this);
            console.log('work');
            //document.addEventListener('blur', ()=>{MiniGame.windowOnBlur(this)} );
            window.onblur=()=>{
                //console.log(this);  if you don't use arrow function ,then this -> window
                //now this -> Pause object -- a React Component
                MiniGame.windowOnBlur(this);
            }
            window.onfocus=()=>{
                MiniGame.windowOnFocus(this);
            }
        }
    
        handleClick(){
            MiniGame.pauseToastClickHandler(this);
        }
    
    
    
        render(){
            const display=this.state.display;
            return(
                <div>
                    <div className={s.toast+' '+s.toastOrigin} 
                            onClick={this.handleClick}
                            style={{display:display}}>
                        <p className={s.pause}>{this.props.pause.info}</p>
                        <p>{this.props.pause.start}</p>
                    </div>
                    <span style={stylePause} 
                                onClick={this.handleClick}>|</span>
                </div>
            );
        }
    }
    
    export default Pause;

    可以看到主要是使用内联样式来显示这个组件,在componentDidMount即组件渲染完成后绑定了
    window.onblur 和window.onfocus两个事件,
    事件处理函数windowOnBlur和windowOnFocus在游戏逻辑中定义,所以其实我们尽量让View的逻辑轻。
    同时也有一个暂停按钮用于恢复或暂停游戏。

    处理函数:

        pauseToastClickHandler=function(that){
            togglePaused(that);
        }
    // 离开浏览器 失去焦点时触发
    function windowOnBlur(that){
       // console.log(loading,gameOver,game.paused);
        if(!loading && !gameOver&& !game.paused){
            togglePaused(that);
            let displayx=game.paused?'inline':'none';
            that.setState({  
                display:displayx
            });
        }
        //console.log('sss');
    }
    
    function windowOnFocus(that){
        if(game.paused&&!gameOver){
            togglePaused(that);
            let displayx=game.paused?'inline':'none';
            that.setState({
                display:displayx
            });
        }
        //console.log('eee');
    }
    
    //Pause and Auto-pause Event handler...................................
        togglePaused=function(that){ //that object in jsx
            game.togglePaused();
            //pausedToast.style.display = game.paused ? 'inline' : 'none';
            let displayx=game.paused?'inline':'none';
        //Checking if game is paused and trigger paused
            if(game.paused){
                Event.trigger('GamePause');
            }else{
                Event.trigger('GameRecover');
            }
            that.setState({
                display:displayx
            });
    
        },

    屏幕失去焦点或者获得焦点时,更新组件的state,即display属性。
    togglePaused处理函数负责处理暂停恢复toggle逻辑,更新了state的同时,
    触发GamePause 和 GameRecover事件。

    在minGame主逻辑中看到:
    利用我们定义好的发布订阅者Event类,来监听这两个自定义事件

    Event.listen('GamePause',()=>{Behavior.PauseHandler(ballSprite)});
    Event.listen('GameRecover',Behavior.RecoverHandler);

    后面我会再介绍这两个事件,主要是处理暂停和恢复的精灵的状态。

    游戏结束

    这里写图片描述

    class GameOver extends React.Component{
        constructor(props){
            super(props);
    
            //this.handleOver=this.handleOver.bind(this);
            this.handleClick=this.handleClick.bind(this);
        }
    
        // handleOver(){
        //     MiniGame.over(this)
        // }
    
        handleClick(){
           // this.props.newGameClickHandler(that);
           this.props.onClick();
        }
    
        render(){
            const display=this.props.overDisplay;
            return(
                <div className={s.Overtoast+' '+s.toast} style={{display:display}} >
                    <p className={s.title}>{this.props.over.title}</p>
                    <p>
                        <input type='checkbox' />
                        {this.props.over.clear}
                    </p>
                    <input type='button' value={this.props.over.button} 
                                                autoFocus='true' onClick={this.handleClick}/>
                </div>
            );
        }
    }
    

    UI的逻辑主要就是现实和隐藏这个over div, 然后就是有个重启游戏的按钮事件处理。

    newGameClickHandler(){
            let overDisplay= MiniGame.newGameClick();
            console.log(overDisplay);
            this.setState({
                overDisplay:overDisplay
            });
        }

    处理函数主要逻辑仍然是在我们的游戏主逻辑模块MiniGame中实现的。
    同时它也会更新这个组件的state状态。

    //New Game..................................
    //Actually this belongs to the over Compoent
    function newGameClick(){
        let str='none';
        //因为setState是异步的,所以其实这里应该promise,等到
        //状态resolved才执行startNewGame()
        setTimeout(()=>{
            startNewGame();
             setTimeout(()=>{
                //这里异步执行
                resetSprite();      
              },500);
        },100);
        return str;
    };
    
    function startNewGame(){
        //highScoreParagraph.style.display = 'none';
        ////让sprite.color最快重置为[] ? 在endGame那里重置才对
        emptyArr();
        gameOver=false;
        livesLeft=1;
        score=0;
        deepCopyColor();  //copy from FILL_STYLES to leftColor
        initalColor();//initialize color
        createStages(); //create totalColor
        //更新分数牌
        Event.trigger('LoadScore');
        //重置sprite应该在ledgeColor更新之后!
    
    }

    重启游戏主要是复位精灵,重置情况之前使用的颜色数组,要重新去生成一个颜色序列数组,并更新游戏的一些状态参数,比如 liveLeft,score等。
    重启这个游戏就会把over的div隐藏起来了。 同样也是通过setState实现,因为React改变一个组件的状态state只能通过setState方法实现!

    其他组件CSS

    其他组件是进度组件和 Score分数组件
    我们后面专门讲React在本项目的应用的时候专门再讲。
    主要也是div的显示和隐藏, 其实组件中定义了多少state,就应该有相应的处理函数来处理。
    这里主要讲一下绘制一个Loading按钮和 绘制气球的CSS
    我们使用气球来作为得到的分数。

    绘制三角形箭头

    这里写图片描述

    $CANVAS_WIDTH:600px;
    
    .Progress-root {
       position:relative;
       width:$CANVAS_WIDTH;
    
    }
    
    .Progress-title {
       font: 16px Arial;
    }
    
    .Progress-root p {
       margin-left: 10px;
       margin-right: 10px;
       color: black;
    }
    
    .Progress-button {
       padding:{
          top: 50%;
       }
       margin:{
          top:50%;
       } 
       padding: 20px;
       position: absolute;
       left:40%;
       top: $CANVAS_WIDTH/2;
       width: 100px;
       height: 80px;
       display: block;
       background:{
          color:white;
       }
       margin:0 auto;
       border-radius:20px;
       box-shadow:1px 1px 5px #999;
    
    }
    .Progress-input{
       position:absolute;
       width:0;
       height:0;
       //20% depends on the parent node 's width
       margin:{
          left:20%;
          top:20%;
       }
       border:25px solid transparent;
       border-left:50px solid #00CC00;
       background-color:transparent;
       content:'  ';
    }

    绘制三角形就是Progress-input绘制,利用border,但是把width和height设置为0
    想象就能知道现在就是4个三角形构成这个border了,我们只需要定义一个border-left即可
    content:’ ’ 是为了兼容性考虑。

    绘制气球

    这里写图片描述

    let styleObj={
        width:'30px',
        height:'30px',
        padding:'15px',
        borderRadius:'120px',
        borderLeft:'1px solid black',
        background:'',
        position:'absolute',
        left:'',
        top:'100px'
    },
    hrObj={
       width:'1px',
       height:'100px',
       position:'absolute',
       top:0,
       left:''
    }

    利用hr标签作为气球的竖线。
    设置它的宽度为1,然后高度足够高,绝对定位。 left会在后面随着加入的气球越来越多而动态修改。
    气球简单的使用borderRadius来设置即可。 同样它的left坐标后面也是要动态修改,还有它的backgroundColor也是会根据精灵得到的颜色来设置。

    React绑定气球改变事件

        componentDidMount(){
            //suitable to set listen
            Event.listen('updateLineBallObj',(realMyObj)=>{
                this.setState({
                    arrObj:realMyObj
                });
                //console.log(realMyObj);
            });
        }

    组件生命周期中的渲染完成后,监听这个updateLineBallObj事件,一旦这个事件发生了
    就说明组件state改变。

    function updateMyRealColor(){ //countRect realMyColor myColor
        let leftOffset=calculLeftOffset();
        if(countRect%10===0&&countRect>=10){
            let color=myColor.shift();
            if(color){ //if color==undefined, then escape it 
                let tmpObj={color:color,left:leftOffset};
                realMyObj.push(tmpObj);
                //console.log(ballSprite.color);
                ballSprite.color.push(color);
                //触发更新分数,事件在ScoreToast中监听
                Event.trigger('LoadScore');
            }
            //realMyColor.push(color);//adding new color 
        }
        if(realMyObj.length<11){//if length >11
            Event.trigger('updateLineBallObj',realMyObj);
        }
    }

    这个处理函数主要就是增加气球,每隔10个矩形增加一个气球,气球即tmpObj对象来存储它的两个属性
    left它的偏移位置以及color它的颜色。 颜色是在myColor中获取。同时精灵ballSprite.color也压入这个颜色,表示当前精灵拥有的颜色, 而得到的分数即气球数量。

    计算气球偏移

    function calculLeftOffset(){
        //len is changing all the time. leftOffset is a local variable
        let len=realMyObj.length,leftOffset;
        if(len===1){
            leftOffset=250;
            return leftOffset;
        }
        if(len<7){ //already draw 1?
            leftOffset=250-(len-1)*45;
        }else {
            leftOffset=250+(len-6)*45;
        }
        return leftOffset;
    }

    根据canvas的宽度,均匀分布这些气球。 设置一个初始偏移,然后后面的气球按照确定的间隔
    跟随在后面。 我的算法是:先将前7个气球绘制在第一个的左边,后面的绘制在右边。
    间隔都是一样的。

    Behavior行为模块设计

    精灵(小矩形)和大矩形的碰撞检测

    逻辑框图
    这里写图片描述

    1. 检测碰撞的同时需要确定精灵是否在ledge大矩形上面
    2. 对于行为的每次在动画循环中执行,每次都先判断小矩形(球)是否在下降
      如果是在下降,那就判断它降落在哪个ledge,也就是碰撞检测,检测在哪个大矩形上面。

    因为我判断碰撞时,应该是有三种情况 (使用identity函数判断)

    • 小矩形刚好落在矩形内,没有超出它的边缘,
    • 小矩形在左侧,压在左边缘
    • 小矩形在右边缘,压在右边缘。

    判断规则
    所以我们需要判断精灵(小矩形)到底在哪个矩形上占据的位置更多,长度更多。

    function identify(sprite,ledge){
        let ledgeRight=ledge.left+ledge.width,
            spriteRight=sprite.left+sprite.width;
        if(ledge.left<=sprite.left&&ledgeRight>=spriteRight){
            //completely inside the ledge!
            return 0;
        }else if(ledgeRight>spriteRight&&ledge.left>sprite.left){
            //over the range of left side ,超出左边边缘了。但仍然算碰撞到
            return -1;
        }else{
            //超出右边边缘了,但仍然是属于碰撞
            return 1;
        }
    }

    本质就是判断精灵的左边坐标,右边坐标和ledge的左边坐标,右边坐标相对位置。

    然后detectRange就是根据这个identity返回的flag,判断这三种情况。

    function detectRange(sprite,ledge,ledgeArr){
        //ledge: current ledge, ledgeArr contains all the ledges 
        //we need to compare current ledge and next ledge
        //We don't know the hitLedge is on the left side of the sprite or the right side. so we need to identify. 
        let index=ledgeArr.indexOf(ledge),
            ledgeRight=ledge.left+ledge.width,
            spriteRight=sprite.left+sprite.width,
            leftWidth,
            rightWidth;
        let flag=identify(sprite,ledge);
        index=1;
        //supposed index===1
        if(flag===0){
            return index;
        }
        //flag===1 means on the right side , -1 means on the left side 
        leftWidth=(flag===1)?(ledge.width-(sprite.left-ledge.left)):
                                        (ledge.left-sprite.left);
        rightWidth=(flag===1)?(spriteRight-ledgeRight):
                                    (ledge.width-(ledgeRight-spriteRight));
    
        if(flag===1){
            //Math.max(leftWidth,rightWidth);//return the larger number
            return (leftWidth>rightWidth)?(index-1):(index);
        }else{
            return (leftWidth>rightWidth)?(index):(index+1);
        }
    }

    如果是情况A,就返回当前下标
    如果是flag===1 表示小球在矩形右侧,那就判断一下,超出了多少,
    如果占据当前矩形的范围更大就返回index,否则返回index+1。 (实际就是求左边占据的宽度leftWidth 和 右边占据的宽度 rightWidth进行比较~)
    如果 flag===-1 表示在矩形ledge左侧,判断方式同上!

    精灵重力和上抛行为

    精灵行为的逻辑框图
    这里写图片描述

    此处检测精灵的位置是个难点,外接矩形 碰撞检测,检测它到底属于哪个矩形上面,然后才是比较颜色。

    (这种预测的检测方式其实不够准确。 )

    暂停与恢复是一个难点:
    使用了一个栈来保存暂停之前的精灵的状态,一个暂停的处理函数,每次暂停时判断一下当前的精灵是否处于
    矩形的上方,然后保存这个精灵的状态,直接整个精灵push到stack里面。 每次暂停恢复就从栈中弹出精灵
    此处弹出的是最后一个精灵状态,不管暂停push压入了多少个sprite状态,只取最后一个精灵状态,包括top
    velocityY等。然后就启动下降的动画计时器。

    定义精灵的物理效果:

    上抛运动

    function tapSpeedingFalling(sprite,fps){
        sprite.top+=sprite.velocityY/fps;///this.fps;
        //falling equation
        //console.log(sprite.velocityY);
        sprite.velocityY=(GRAVITY_FORCE)*
            (fallingAnimationTimer.getElapsedTime()/1000) + TAP_VELOCITY;
        // console.log(sprite.velocityY);
        if(sprite.top>canvas.height){
            stopFalling(sprite);
        }      
    }

    普通下落

    function normalFalling(sprite,fps){
        sprite.top+=sprite.velocityY/fps;
        sprite.velocityY=(GRAVITY_FORCE)*
                        (fallingAnimationTimer.getElapsedTime()/1000);
        if(sprite.top>canvas.height){//直到大于canvas.height
            stopFalling(sprite);
        }
    }

    游戏演示地址

    www.linzhida.cc

    项目github地址
    https://github.com/spade69/lonelyRoutine

    展开全文
  • echarts 暂时不支持圆角的圆环 下面是封装的组件源码: import React, { Component }...export class CanvasDemo extends React.Component { constructor(props) { super(props) this.initCanvas = this.initC...

    echarts 暂时不支持圆角的圆环
    下面是封装的组件源码:

    import React, { Component } from 'react';
    
    export class CanvasDemo extends React.Component {
      constructor(props) {
        super(props)
        this.initCanvas = this.initCanvas.bind(this)
      }
      initCanvas() {
        const {
          x0,//原点坐标
          y0,
          r,// 半径
          lineWidth, // 画笔宽度
          strokeStyle, //画笔颜色
          LinearGradientColor1, //起始渐变颜色
          LinearGradientColor2, //结束渐变颜色
          Percentage,// 进度百分比
        } = this.props
        let ele = document.getElementById("time_graph_canvas")
        let circle = ele.getContext("2d");
        //创建背景圆
        circle.lineWidth = lineWidth;
        circle.strokeStyle = strokeStyle;
        circle.lineCap = 'round';
        circle.beginPath();//开始一个新的路径
        circle.arc(x0, y0, r, 0, 2 * Math.PI, false);///用于绘制圆弧context.arc(x坐标,y坐标,半径,起始角度,终止角度,顺时针/逆时针)
        circle.stroke();//对当前路径进行描边
        //创建渐变圆环
        let g = circle.createLinearGradient(x0, 0, x0 + r * Math.cos(Percentage * (Math.PI * 2)), y0 + r * Math.sin(this.props.Percentage * (Math.PI * 2)));  //创建渐变对象  渐变开始点和渐变结束点
        g.addColorStop(0, LinearGradientColor1); //添加颜色点
        g.addColorStop(1, LinearGradientColor2);
        circle.lineWidth = lineWidth //设置线条宽度
        circle.lineCap = 'round';
        circle.strokeStyle = g;
        circle.beginPath();//开始一个新的路径
        circle.arc(x0, y0, r, -Math.PI / 2, -Math.PI / 2 - Percentage * (Math.PI * 2), true);
        circle.stroke();//对当前路径进行描边
      }
    
      componentDidMount() {
        this.initCanvas()
      }
      componentDidUpdate() {
        this.initCanvas()
      }
      static defaultProps = {
        canvaswidth: 160,// 画布宽度
        canvasheight: 160,// 画布高度
        x0: 80,
        y0: 80,
        r: 72,
        lineWidth: 16,
        strokeStyle: 'rgba(248, 248, 248, 1)',
        LinearGradientColor1: '#3EECED',
        LinearGradientColor2: '#499BE6'
      }
      render() {
        const { width, height, canvaswidth, canvasheight } = this.props
        return (
          <div style={{ width: width, height: height, padding: 10 }}>
            <canvas id="time_graph_canvas" width={canvaswidth} height={canvasheight}></canvas>
          </div>
        )
      }
    }

    效果图:
    这里写图片描述

    展开全文
  • import React, { Component } from 'react'; function rect(props) { const { ctx, fromX, fromY, toX, toY, color } = props; var headlen = 10; //自定义箭头线的长度 var theta = 30; //自定义箭头...

    LineRow.js :

    import React, { Component } from 'react';
    
    
    function rect(props) {
    
        const { ctx, fromX, fromY, toX, toY, color } = props;
        var headlen = 10; //自定义箭头线的长度
        var theta = 30; //自定义箭头线与直线的夹角,个人觉得45°刚刚好
        var arrowX, arrowY; //箭头线终点坐标
        // 计算各角度和对应的箭头终点坐标
        var angle = Math.atan2(fromY - toY, fromX - toX) * 180 / Math.PI;
        var angle1 = (angle + theta) * Math.PI / 180;
        var angle2 = (angle - theta) * Math.PI / 180;
        var topX = headlen * Math.cos(angle1);
        var topY = headlen * Math.sin(angle1);
        var botX = headlen * Math.cos(angle2);
        var botY = headlen * Math.sin(angle2);
        ctx.beginPath();
        //画直线
        ctx.moveTo(fromX, fromY);
        ctx.lineTo(toX, toY);
    
        arrowX = toX + topX;
        arrowY = toY + topY;
        //画上边箭头线
        ctx.moveTo(arrowX, arrowY);
        ctx.lineTo(toX, toY);
    
        arrowX = toX + botX;
        arrowY = toY + botY;
        //画下边箭头线
        ctx.lineTo(arrowX, arrowY);
    
        ctx.strokeStyle = color;
        ctx.stroke();
    }
    
    class LineRow extends Component {
        componentDidMount() {
            this.updateCanvas();
        }
        componentDidUpdate() {
            this.updateCanvas();
        }
        updateCanvas() {
            try {
                var canvas = document.getElementById('canvas1');
    
    
                if (!canvas) { console.log('Canvas not found.'); }
                else {
    
                    if (!canvas.getContext) { console.log('Context not supported.'); }
                    else {
                        const ctx = this.refs.canvas.getContext('2d');
                        if (!ctx) { console.log('Context 2D not available.'); }
                        else {
                        	// 调用react函数, 从坐标(0,0)到(200,200)处, 颜色为黑色.   
                            rect({ ctx, fromX: 0, fromY: 0, toX: 200, toY: 200, color: "#000" });
                        }
                    }
                }
            }
            catch (exc) { console.log(exc); }
        }
        
        render() {
            return (
                <div>
                    <canvas id="canvas1" ref="canvas" width={300} height={300} />
                </div>
            )
        }
    }
    
    export default LineRow;
    

    index.js :

    import React from 'react';
    import ReactDOM from 'react-dom';
    
    
    import LineRow from './LineRow'
    
    ReactDOM.render(
      <React.StrictMode>
        { <LineRow />}
      </React.StrictMode>,
      document.getElementById('root')
    );
    

    效果:

    image-20200413131027416

    展开全文
  • 最近做个移动端视频需求,要求隐藏播放控件,并且可以自动...受以前做的一个利用canvas做视频直播的项目启发,尝试下canvas做视频播放,于是我抱着试一试的心态去查阅了相关资料,尼玛,还真的可以,而且原理很简单...

    最近做个移动端视频需求,要求隐藏播放控件,并且可以自动播放而且隐藏播放控件(不太人性化),最后要有个定制的结束遮罩层用来人机交互。尝试直接用video标签做,但是各种坑啊,video永远是在页面的最顶层,所以播放控件的自定义化就凉凉了,怎么办呢?受以前做的一个利用canvas做视频直播的项目启发,尝试下canvas做视频播放,于是我抱着试一试的心态去查阅了相关资料,尼玛,还真的可以,而且原理很简单!

     

    首先要解决在react中操作canvas的问题,众所周知,react和vue都是生成的虚拟dom,直接通过dom的API操作canvas是不现实的。npm上的一些库也是繁琐的像XX。。。我写了一个方法,在react的componentDidMount中利用react的ref把canvas直接传给这个方法,拿到了页面上的canvas,只要这个页面不卸载,我就可以为所欲为啦!直接上代码:

    import React, { Component } from 'react';
    import './App.css';
    import {draw} from './canvas'
    class App extends Component {
    
      componentDidMount() {
        if (this.canvas) {
            draw(this.canvas)
        }
      }
    
      render() {
    
        return <div style={{width: '100%',height: 'auto'}} id={'scrollBox'}>
          <canvas width='1280' height='720' ref={node => this.canvas = node} style={{width: '100%',height: 'auto'}}></canvas>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
          <p>我是测试我是测试</p>
        </div>;
      }
    }
    
    export default App;
    

       通过ref函数把canvas赋值给this.canvas,在componentDidMount钩子中传给canvas脚本文件绘制(注意,由状态动态生成的canvas可能拿不到这个dom,建议放到componentDidUpdate钩子中,不熟悉react生命周期的同学自行查阅官方文档),于是这个时候,我拿出了我的终极大杀器——canvas.js.好了,不吹牛皮,其实它就是一个非常简单的脚本,看看它的代码:

    export const draw = (canvas) => {
        if (canvas) {
            //获取canvas上下文
            let ctx = canvas.getContext('2d');
    
            //创建video标签,并且设置相关属性
            let video = document.createElement('video');
    
            video.preload = true;
            video.autoplay = true;
            video.src='https://pic.ibaotu.com/00/92/91/90f888piCjkw.mp4';
            //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();
                } else {
                    video.play();
                }
            },false);
        }
    }
    

      大概的思路就是我首先在内存中创建一个video标签并设置好相关属性,之后监听video的play事件,一旦开始播放,我就会通过requestAnimationFrame把video逐帧绘制到canvas上。由于真的有个video在播放视频,所以你会听到声音,但是,我并没有把它放到页面上,所以你看到的不是视频,而是一个没有声音的canvas!嘿嘿嘿O.o

      视频这样子处理,虽然依旧不能跨过谷歌和苹果爸爸对视频自动播放的限制,但是可以像操作普通div节点一样操作视频了,尤其是定制视频交互控件的样式,基本都可以满足产品的各种无厘头需求啦!最后献上我的页面

     

    注意是有声音的‘视频’哦!

    转载于:https://www.cnblogs.com/zhangbob/p/10039440.html

    展开全文
  • react html2canvas生成pdf

    2020-03-25 17:18:10
    html2canvas(document.getElementById("_container")).then(function (canvas) { const contentWidth = canvas.width; const contentHeight = canvas.height; const pageData = canvas.toDat...
    import jsPDF from 'jspdf';
    import html2canvas from 'html2canvas';
    
      html2canvas(document.getElementById("_container")).then(function (canvas) {
           const contentWidth = canvas.width;
           const contentHeight = canvas.height;
           const pageData = canvas.toDataURL('image/jpeg', 1.0);
           const pdfX = (contentWidth + 10) / 2 * 0.75;
           const pdfY = (contentHeight + 500) / 2 * 0.75 ;// 500为底部留白
    
           const imgX = pdfX;
           const imgY = (contentHeight / 2 * 0.75); //内容图片这里不需要留白的距离
    
           const PDF = new jsPDF('', 'pt', [pdfX, pdfY]);
    
           PDF.addImage(pageData, 'jpeg', 0, 0, imgX, imgY);
           PDF.save('download.pdf');
         });
    
    展开全文
  • canvas-nest react版本

    2018-01-10 14:00:00
    canvas-nest是一个背景动画,源代码地址是https://github.com/hustcc/canvas-nest.js,最近最react项目想用这个特效,觉得不是很好配置,故改动了一下,如果侵权请联系本人删除。 使用方法, 1.import ...
  • import React, { Component } from 'react' // import ClassNames from 'classnames' import PropTypes from 'prop-types' import _ from 'lodash' import './index.less' class DrawS...
  • 首先自行安装一下 html2canvas & jspdf import html2canvas from 'html2canvas'; import jsPDF from 'jspdf'; export default ( target, pdfName = 'guanrongjia', successCallback = () => {}, ...
  • 最近react 官方 2018 ReactConf 大会上宣布 React v16.7.0-alpha(内测) 将引入 Hooks。所以我们有必要了解 Hooks,以及由此引发的疑问。 React Hooks相关推荐 30分钟精通React今年最劲爆的新特性——React Hooks...
  • 游戏的开发界有一个理论,就是当动画或者交互响应达到60FPS(60帧每秒)的时候,就可以定义为流畅,按此理论,那么每帧里所有操作必须16ms完成。要想提高页面的用户体验,必须性能上下功夫。最早做动画都是用 ...
  • 项目需要实现将某块html生成img图片,供用户保存到本地,最终采用html2canvas实现。1、如何引入html2canvas? 项目架构是vue-cli的单页应用,如果index.html入口主文件里面引入&lt;script src='...
  • 导入html2canvas和jspdf插件到项目 ...引入html2canvas和jspdf到jsx import jsPDF from 'jspdf'; import html2canvas from 'html2canvas' 定义一个div和需要生成pdf的页面 <div id="demo">...
  • 我的处女作《Canvas系列教程》我的Github上正在连载更新,希望能得到您的关注和支持,让我有更多的动力进行创作。 教程介绍、教程目录等能README里查阅。...在react+antd项目实现指定区域的打印功...
  • 开发官网等一些偏静态类的网站时,经常会碰到某个组件需要用到多张图片,正常引入方式如: import demoBJ from '../../images/demo1.jpg' 所产生的问题就是如果我用到了十张图片资源,同类代码就需要写上十次...
  • 用video-react插入视频

    2019-07-09 10:13:43
    在react中引入 下载视频插件 npm install –save video-react 或yarn add video-react 引入 import { Player } from ‘video-react’; import “node_modules/video-react/dist/video-react.css”; //渲染样式 ...
  • 官网http://html2canvas.hertzen.com/ 安装 Install NPM npm install --save html2canvas ...引入 importhtml2canvasfrom'html2canvas' 页面 需要打印的地方 加上id <Cardid="views...
  • react中动态生成二维码(qrcode.react 的使用) 先进行包安装 npm install qrcode.react 使用它 import React from 'react' import QRCode from 'qrcode.react' React.render( <QRCode value=...
  • React安装引入

    2019-10-11 10:30:55
    目录React特点React安装引入JSX语法规则ES6 React特点 1.声明式编码(react,vue)数据驱动视图 jQuery(命令式编码) 2.组件化编码 3.一次运行,随处编写(可以服务端编写) 4.高效 虚拟DOM 5.单向数据流(vue,react) ...
  • 由于手头的icon有限,需要使用更多的图标,就得找外援: 1、react安装icon插件,使用插件里已经有的图标 ...Include popular icons in your React projects easly with react-icons, which utilizes ES6 impo...
  • 借助插件生成简单类型的二维码 使用的插件:qrcode.react ...引入: const QRCode = require('qrcode.react'); 使用: <QRCode value={`hello world`} size={120} /> 结果: ** –save 可以把插件...
1 2 3 4 5 ... 20
收藏数 2,221
精华内容 888