HTML5游戏开发(九)

HTML5游戏开发(九)

一、图像绘制

  HTML5的Canvas元素提供了极为丰富的图像支持。我们可以在绘制的时候缩放或保持原样,可以将图片绘制在canvas中的任何地主,也可以操作每个像素的颜色及透明度。
图像绘制

方法描述
drawImage()向画布上绘制图像、画布或视频

像素操作

属性描述
width返回 ImageData 对象的宽度
height返回 ImageData 对象的高度
data返回一个对象,其包含指定的 ImageData 对象的图像数据
方法描述
createImageData()创建新的、空白的 ImageData 对象
getImageData()返回 ImageData 对象,该对象为画布上指定的矩形复制像素数据
putImageData()把图像数据(从指定的 ImageData 对象)放回画布上

1.图像绘制

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
         <title>图像绘制</title>
        <style>
            #canvas {
                margin-left: 20px;
                margin-right: 0;
                margin-top: 10px;
                margin-bottom: 20px;
                border: thin solid #aaaaaa;
                cursor: crosshair;
                padding: 0;
            }
        </style>
    </head>

    <body>
        <canvas id='canvas' width='600' height='317'>
     </canvas>
        <script src='js/drawing.js'></script>
    </body>
</html>

JS脚本

var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    //创建图像对象
    image = new Image();

image.src = 'img/xueshan.png';
image.onload = function(e) {
   context.drawImage(image, 0, 0);
};

2.图像的缩放

var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    //创建图像对象
    image = new Image();

image.src = 'img/xueshan.png';
image.onload = function(e) {
   //原图为600x317将其缩放为500X300
   context.drawImage(image, 0, 0,500,300);
};

显示效果:
image

3.离屏

  离屏canvas经常用来存放临时性的图像信息。使用离屏可以提高绘图效率。

(1)图像加载
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>图像绘制</title>
        <style>
            #canvas {
                margin-left: 20px;
                margin-right: 0;
                margin-top: 10px;
                margin-bottom: 20px;
                border: thin solid #aaaaaa;
                padding: 0;
            }
        </style>
    </head>
    <body>
        <div id='controls'>
            <output id='scaleOutput'>1.0</output>
            <input id='scaleSlider' type='range' min='1' max='3.0' step='0.01' value='1.0' />
        </div>
        <canvas id='canvas' width='600' height='317'>
     </canvas>
        <script src='js/drawing.js'></script>
    </body>
</html>

JS脚本:

var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    //创建离屏对象
    offscreenCanvas = document.createElement('canvas');
    offscreenContext = offscreenCanvas.getContext('2d'),
    image = new Image(),
    //获取输出对象
    scaleOutput = document.getElementById('scaleOutput'),
    //缩放比例对象
    scaleSlider = document.getElementById('scaleSlider'),
    scale = scaleSlider.value,
    scale = 1.0,
    MINIMUM_SCALE = 1.0,
    MAXIMUM_SCALE = 3.0;


image.src = 'img/xueshan.png';
//离屏的大小设定
offscreenCanvas.width = canvas.width;
offscreenCanvas.height = canvas.height;
//图像加载
image.onload = function(e) {
   context.drawImage(image, 0, 0, canvas.width, canvas.height);
   //使用离屏---给离屏对象一份数据
   offscreenContext.drawImage(image, 0, 0,
                              canvas.width, canvas.height);
    //初始绘制
    drawWatermark(context);
   //绘制水印--使用离屏绘制
   drawWatermark(offscreenContext);
   //图像缩放
   drawScaleText(scaleSlider.value);
};
(2)图像缩放
//图像缩放
function drawScaled() {
   var w = canvas.width,
       h = canvas.height,
       sw = w * scale,
       sh = h * scale;
   //使用离屏图像绘制
   context.drawImage(offscreenCanvas, 0, 0,
      offscreenCanvas.width, offscreenCanvas.height,
      -sw/2 + w/2, -sh/2 + h/2, sw, sh);
}
//设置缩放文本值
function drawScaleText(value) { 
   var text = parseFloat(value).toFixed(2);
   //计算比例
   var percent = parseFloat(value - MINIMUM_SCALE) /
                 parseFloat(MAXIMUM_SCALE - MINIMUM_SCALE);
    //输出文本
   scaleOutput.innerText = text;
   percent = percent < 0.35 ? 0.35 : percent;
   //输出字体大小
   scaleOutput.style.fontSize = percent*MAXIMUM_SCALE/1.5 + 'em';
}
(3)绘制水印
//绘制水印
function drawWatermark(context) {
   var lineOne = 'Copyright',
       lineTwo = 'marquis',
       textMetrics = null,
       FONT_HEIGHT = 80;

   context.save();
   context.fillStyle = 'rgba(100,140,230,0.5);';
   context.strokeStyle = 'yellow';
   context.shadowColor = 'rgba(50, 50, 50, 1.0)';
   context.shadowOffsetX = 5;
   context.shadowOffsetY = 5;
   context.shadowBlur = 10;

   context.font = FONT_HEIGHT + 'px Arial';
   //绘制第一行文本
   textMetrics = context.measureText(lineOne);
   context.translate(canvas.width/2, canvas.height/2);
   context.fillText(lineOne, -textMetrics.width/2, 0);
   context.strokeText(lineOne, -textMetrics.width/2, 0);
   //绘制第二行文本
   textMetrics = context.measureText(lineTwo);
   context.fillText(lineTwo, -textMetrics.width/2, FONT_HEIGHT);
   context.strokeText(lineTwo, -textMetrics.width/2, FONT_HEIGHT);
  context.restore();
}
(4)事件处理
//-----------------------事件处理
scaleSlider.onchange = function(e) {
   scale = e.target.value;

   if (scale < MINIMUM_SCALE) scale = MINIMUM_SCALE;
   else if (scale > MAXIMUM_SCALE) scale = MAXIMUM_SCALE;
    //图像缩放
   drawScaled();
   //文本缩放
   drawScaleText(scale);
}

显示效果:
image

二、图像的操作

1.底片效果

<!DOCTYPE html>
<html>
   <head>
     <title>底片滤镜</title>
     <meta charset="utf-8" />
      <style> 
         body {
            background: rgba(100, 145, 250, 0.3);
         }

         #canvas {
            margin-left: 20px;
            margin-right: 0;
            margin-bottom: 20px;
            border: thin solid #aaaaaa;
            cursor: crosshair;
         }

         #controls {
            margin: 20px 0px 20px 20px;
         }

         a {
            font: 18px Times Roman;
            text-decoration: none;
            margin-right: 15px;
         }
      </style>
   </head>
  <body>
      <div id='controls'>
         <input type='button' id='negativeButton' 
                value='底片'/>
      </div>
      <canvas id='canvas' width='600' height='317'>
      </canvas>
     <script src='js/negative.js'></script>
  </body>
</html>

JS脚本

var image = new Image(),
    canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    negativeButton = document.getElementById('negativeButton');

negativeButton.onclick = function() {
    //获取图像数据
   var imagedata = context.getImageData(0, 0, canvas.width, canvas.height),
    //图像数据处理,注意:一定要在服务中运行,否则会有跨域问题   
    data = imagedata.data;
   //底片滤镜,计算出平均值,将这一平均值设置回去
   for(i=0; i <= data.length - 4; i+=4) {
      data[i]   = 255 - data[i]
      data[i+1] = 255 - data[i+1];
      data[i+2] = 255 - data[i+2];
   }
   context.putImageData(imagedata, 0, 0);
};
//加载图片
image.src = 'img/xueshan.png';
image.onload = function() {
   context.drawImage(image, 0, 0,
                     image.width, image.height, 0, 0, 
                     context.canvas.width, context.canvas.height);
};

显示效果:
image

错误解决:
example.js:98 Uncaught SecurityError: Failed to execute ‘getImageData’ on ‘CanvasRenderingContext2D’: The canvas has been tainted by cross-origin data.
原因:
getImageData此方法不允许操作非此域名外的图片资源,即使是子域也不行。
解决:
可以使用base64编码方式。
或:
在response添加 Access-Control-Allow-Origin
或使用chrom命令:
–allow-file-access-from-files
image

2.使用工作线程处理图像

(1)基本功能
<!DOCTYPE html>
<html>
   <head>
     <title>凸透镜r</title>
     <meta charset="utf-8" />
      <style> 
         body {
            background: rgba(100, 145, 250, 0.3);
         }

         #canvas {
            margin-left: 20px;
            margin-right: 0;
            margin-bottom: 20px;
            border: thin solid #aaaaaa;
         }
         #controls {
            margin: 20px 0px 20px 20px;
         }

         a {
            font: 18px Times Roman;
            text-decoration: none;
            margin-right: 15px;
         }
      </style>
   </head>
  <body>
      <div id='controls'>
         <input type='button' id='sunglassButton' 
                value='凸透镜'/>
      </div>
      <canvas id='canvas' width='600' height='317'>
      </canvas>
     <script src='js/sunglass.js'></script>
  </body>
</html>

JS脚本

var image = new Image(),
    canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    sunglassButton = document.getElementById('sunglassButton'),
    sunglassFilter,
    sunglassesOn = false;

//凸透镜函数
function putSunglassesOn() {
   //主线程向工作线程发送消息
   sunglassFilter.postMessage(context.getImageData(0, 0, canvas.width, canvas.height));
   //工作线程成功完成工作后的回调函数
   sunglassFilter.onmessage = function (event) {
       context.putImageData(event.data, 0, 0);
   };
}
//绘制原图
function drawOriginalImage() {
   context.drawImage(image, 0, 0,
           image.width, image.height, 0, 0,
           canvas.width, canvas.height);
}
//转换事件
sunglassButton.onclick = function() {
   if (sunglassesOn) {
      sunglassButton.value = '凸透镜';
      drawOriginalImage();
      sunglassesOn = false;
   }
   else {
      sunglassButton.value = '原图';
      //绘制凸透镜效果
      putSunglassesOn();
      sunglassesOn = true;
   }
};

//加载图像
image.src = 'img/xueshan.png';
image.onload = function() {
     //创建一个工作线程,这个线程在主线程外执行
     //注意:js文件引用路径,以html引用路径为准
     //只能用一个js文件创建工作线程,而不能用函数。
     //因为规定工作线程不能访问DOM,如果向Worker构造
     //函数传入一个函数,该函数可能包含DOM或主JavaScript代码
     //的引用,就违反规则。因此工作线程的设计者选择的做法是只
     //能传递一个js文件的URL
    sunglassFilter = new Worker('js/sunglassFilter.js');
    drawOriginalImage();
};

(2)工作线程 sunglassFilter.js
//定义一个message事件
onmessage = function (event) {
    console.log(event)
   var imagedata = event.data,
       data = imagedata.data,
       length = data.length,
       width = imagedata.width;

   for (i=0; i < length; ++i) {
      if ((i+1) % 4 != 0) {   
         if ((i+4) % (width*4) == 0) { //最后一个像素
            data[i] = data[i-4];
            data[i+1] = data[i-3];
            data[i+2] = data[i-2];
            data[i+3] = data[i-1];
            i+=4;
         }
         else {
           data[i] = 2*data[i] - data[i+4] - 0.5*data[i+4];
         }
      }
   }
   //调用postMessage方法,window.postMessage() 方法可以安全地实现跨源通信。
   postMessage(imagedata);
};

显示效果:
image

四、视频

  研究Canvas视频功能的最终目标是为了实现即时视频处理。

1.播放视频

requestNextAnimationFrame函数,将视频中的当前视频帧绘制到Canvas中,以实现视频播放。

(1)Ployfill函数定义
/**
 * ployfill函数定义
 */
window.requestNextAnimationFrame =
    (function() {
        var originalWebkitRequestAnimationFrame = undefined,
            wrapper = undefined,
            callback = undefined,
            geckoVersion = 0,
            userAgent = navigator.userAgent,
            index = 0,
            self = this;
        if(window.webkitRequestAnimationFrame) {
            // Define the wrapper
            wrapper = function(time) {
                if(time === undefined) {
                    time = +new Date();
                }
                self.callback(time);
            };
            originalWebkitRequestAnimationFrame = window.webkitRequestAnimationFrame;
            window.webkitRequestAnimationFrame = function(callback, element) {
                self.callback = callback;
                originalWebkitRequestAnimationFrame(wrapper, element);
            }
        }
        if(window.mozRequestAnimationFrame) {
            index = userAgent.indexOf('rv:');
            if(userAgent.indexOf('Gecko') != -1) {
                geckoVersion = userAgent.substr(index + 3, 3);
                if(geckoVersion === '2.0') {
                    window.mozRequestAnimationFrame = undefined;
                }
            }
        }
        return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||

            function(callback, element) {
                var start,
                    finish;

                window.setTimeout(function() {
                    start = +new Date();
                    callback(start);
                    finish = +new Date();

                    self.timeout = 1000 / 60 - (finish - start);

                }, self.timeout);
            };
    })();
(2)视频播放
<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>视频播放</title>
        <style>
            body {
                background: #dddddd;
            }

            #canvas {
                background: #ffffff;
                border: thin solid darkgray;
            }

            #video {
                /*将视频设置为不显示*/
                display: none;
            }
        </style>
    </head>
    <body>
        <!--添加视频标记-->
        <video id='video'  width="320" height="240" controls="controls">
            <source src='video/movie.ogg' />
        </video>
        <canvas id='canvas' width='600' height='405'>
        </canvas>
        <script src='js/video.js'></script>
    </body>

</html>

JS脚本

var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    video = document.getElementById('video');

//播放视频
function animate() {
    if(!video.ended) {
        context.drawImage(video, 0, 0, canvas.width, canvas.height);
        window.requestNextAnimationFrame(animate);
    }
}
//播放视视
video.play();
//将播放的视频放入canvas中
window.requestNextAnimationFrame(animate);

显示效果:
image

2.视频处理

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>视频处理</title>
        <style>
            body {
                background: #dddddd;
            }
            .floatingControls {
                position: absolute;
                left: 175px;
                top: 290px;
            }

            #canvas {
                background: #ffffff;
                border: thin solid #aaaaaa;
                margin: 10px;
            }
        </style>
    </head>
    <body>
        <video id='video' controls src='video/movie.ogg'></video>
        <canvas id='canvas' width='320' height='240'>
        </canvas>
        <div id='controls' class='floatingControls'>
            <input id='controlButton' type='button' value='播放' />
            <input id='colorCheckbox' type='checkbox' checked> <span style='font-size:1.15em'>颜色</span>
            <input id='flipCheckbox' type='checkbox'> <span style='font-size:1.15em'>翻转</span>
        </div>
        <script src='js/videoclip.js'></script>
    </body>
</html>

JS脚本

/**
 * ployfill函数定义
 */
window.requestNextAnimationFrame =
    (function() {
        var originalWebkitRequestAnimationFrame = undefined,
            wrapper = undefined,
            callback = undefined,
            geckoVersion = 0,
            userAgent = navigator.userAgent,
            index = 0,
            self = this;
        if(window.webkitRequestAnimationFrame) {
            // Define the wrapper
            wrapper = function(time) {
                if(time === undefined) {
                    time = +new Date();
                }
                self.callback(time);
            };
            originalWebkitRequestAnimationFrame = window.webkitRequestAnimationFrame;
            window.webkitRequestAnimationFrame = function(callback, element) {
                self.callback = callback;
                originalWebkitRequestAnimationFrame(wrapper, element);
            }
        }
        if(window.mozRequestAnimationFrame) {
            index = userAgent.indexOf('rv:');
            if(userAgent.indexOf('Gecko') != -1) {
                geckoVersion = userAgent.substr(index + 3, 3);
                if(geckoVersion === '2.0') {
                    window.mozRequestAnimationFrame = undefined;
                }
            }
        }
        return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||

            function(callback, element) {
                var start,
                    finish;

                window.setTimeout(function() {
                    start = +new Date();
                    callback(start);
                    finish = +new Date();

                    self.timeout = 1000 / 60 - (finish - start);

                }, self.timeout);
            };
    })();

var canvas = document.getElementById('canvas'),
    offscreenCanvas = document.createElement('canvas'),
    offscreenContext = offscreenCanvas.getContext('2d'),
    context = canvas.getContext('2d'),
    video = document.getElementById('video'),
    controlButton = document.getElementById('controlButton'),
    flipCheckbox = document.getElementById('flipCheckbox'),
    colorCheckbox = document.getElementById('colorCheckbox'),
    imageData,
    poster = new Image();

//-----------------------------移除颜色
//移除颜色
function removeColor() {
    var data,
        width,
        average;
    //获取离屏数据
    imageData = offscreenContext.getImageData(0, 0,
        offscreenCanvas.width, offscreenCanvas.height);

    data = imageData.data;
    width = data.width;

    for(i = 0; i < data.length - 4; i += 4) {
        average = (data[i] + data[i + 1] + data[i + 2]) / 3;
        data[i] = average;
        data[i + 1] = average;
        data[i + 2] = average;
    }

    offscreenContext.putImageData(imageData, 0, 0);
}
//-----------------------------视频翻转
function drawFlipped() {
    context.save();
    context.translate(canvas.width / 2, canvas.height / 2);
    context.rotate(Math.PI);
    context.translate(-canvas.width / 2, -canvas.height / 2);
    context.drawImage(offscreenCanvas, 0, 0);

    context.restore();
}
//下一个视频
function nextVideoFrame() {
    //如果视频已到达结尾时,将按钮重置为播放
    if(video.ended) {
        controlButton.value = '播放';
    } else {
        //获取数据到离屏
        offscreenContext.drawImage(video, 0, 0);
        //如果颜色选中,则去色
        if(!colorCheckbox.checked)
            removeColor();
        //如果反转选中,则进行视每帧反转
        if(flipCheckbox.checked)
        //反转
            drawFlipped();
        else
            context.drawImage(offscreenCanvas, 0, 0);
        //转变后的进行播放
        requestNextAnimationFrame(nextVideoFrame);
    }
}
//播发
function startPlaying() {
    //将视频播放传为Canvas
    requestNextAnimationFrame(nextVideoFrame);
    if(video.ended) {
        video.load();
    }
    //播发
    video.play();
}
//暂停
function stopPlaying() {
    //暂停
    video.pause();
}

//-----------------------------事件处理
controlButton.onclick = function(e) {
        if(controlButton.value === '播放') {
            console.log("播放")
                //开始播发
            startPlaying();
            controlButton.value = '暂停';
        } else {
            //停止播放
            stopPlaying();
            controlButton.value = '播放';
        }
    }
    //poster为video的属性,规定视频下载时显示的图像,或者在用户点击播放按钮前显示的图像。
poster.onload = function() {
    //这里显示一张雪山图
    context.drawImage(poster, 0, 0);
};

//-------------------1、初始化
poster.src = 'img/xueshan.png';
//离屏显示
offscreenCanvas.width = canvas.width;
offscreenCanvas.height = canvas.height;

显示效果:
image

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值