「用前端重返童年🥤」为黑神话悟空定制红白机版游戏开始动画

简介: 「用前端重返童年🥤」为黑神话悟空定制红白机版游戏开始动画

正篇 — 用前端重返童年


网络异常,图片无法展示
|


我之前没有用过canvas,也是一点一点写的,代码质量不要吐槽哈~因为我知道这一次代码质量不行🌟


Hancao present to Game Science in 2021.


既然要出的是一个有情怀的前端作品,我也是花了很长时间在设计上,也试了很多方法去体现我的创意,但是效果并不是很好,边coding边摸索,最终就出来了本次作品的设计,正如我的标题所说,梦回童年的前端作品,效仿的便是老一代插卡游戏机中游戏的开机动画,不知道现在从事前端行业的伙伴年纪如何,但是像我这样刚入行一年的新人都见过,大家应该,八成,或许,大概,也都见过吧...


下面让我们一起见证,寒草用 前端技术黑神话悟空童年游戏经典开始动画结合的作品吧

这是前端工程师寒草在2021年献给游戏科学创作者们的礼物


重返80年代马赛克化的悟空

网络异常,图片无法展示
|


既然我们要重返童年,那么我们这个图片要紧跟时代啊,哪可以这么清晰。回想一下那个年代的游戏都是一个色块一个色块的马赛克,所以我们需要把悟空'马赛克化'

我们先搞一个canvas


<canvas id="my-canvas-monkey-king"></canvas>


之后我们在画布导入图片,并获取点阵信息,并间隔12像素去获取颜色并绘制


// 显示孙悟空
let canvas2 = document.getElementById("my-canvas-monkey-king");
let ctx2 = canvas2.getContext("2d");
var image2 = new Image();
image2.src = "monkey-king.jpeg";
image2.width = 700;
image2.height = 700;
image2.onload = function () {
    canvas2.width = image2.width;
    canvas2.height = image2.height;
    ctx2.drawImage(image2, 0, 0);
    var imageData2 = ctx2.getImageData(0, 0, image2.width, image2.height).data;
    // 将画布背景图黑
    ctx2.fillStyle = "#000";
    ctx2.fillRect(0, 0, image2.width, image2.height);
    var gap = 12;
    for (var h = 0; h < image2.height; h += gap) {
      for (var w = 0; w < image2.width; w += gap) {
        var position = (image2.width * h + w) * 4;
        var r = imageData2[position], g = imageData2[position + 1], b = imageData2[position + 2];
        // 因为我用的原图背景不是纯黑的,所以我直接筛除rgb相加小于165的点(为什么是165呢,因为600是整数,255 * 3 - 600 = 165)
        if (765 - (r + g + b) < 600) {
          ctx2.fillStyle = `rgb(${r}, ${g}, ${b})`;
          ctx2.fillRect(w, h, gap, gap);
        }
      }
    }
}


文字动效设计:悟空!出来吧~

网络异常,图片无法展示
|


这里做了一个logo马赛克化并且逐行绘制的效果,马赛克化的方法和之前一致,这个逐行绘制其实加入了一些细节,越到上面绘制起来越快,有点像一层层摞起来的文字一样~

我们再搞一个canvas


<canvas id="my-canvas-wukong"></canvas>


之后我们获取图片信息,按行存储在数组里,之后用定时器去获取每一行的信息,逐行绘制,每一次执行定时器的回调方法的时候也要记得减少定时器的时长。


let canvas = document.getElementById("my-canvas-wukong");
let ctx = canvas.getContext("2d");
var image = new Image();
image.src = "wukong.png";
image.width = 240;
image.height = 240;
image.onload = function () {
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0);
var imageData = ctx.getImageData(0, 0, image.width, image.height).data;
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, image.width, image.height);
let pointPixels = [];
let rowPoints = [];
for (var h = image.height - 3; h >= 0; h -= 3) {
  if (h !== image.height - 3) {
    pointPixels.push(rowPoints);
    rowPoints = [];
  }
  for (var w = image.width - 3; w >= 0; w -= 3) {
    var position = (image.width * h + w) * 4;
    var r = imageData[position], g = imageData[position + 1], b = imageData[position + 2];
    if (r + g + b !== 0) {
      rowPoints.push([w, h]);
    }
  }
}
ctx.fillStyle = "#fff";
let index = 0;
const length = pointPixels.length;
let delay = 30;
const fn = () => {
  let timer = setTimeout(() => {
    clearTimeout(timer);
    if (index != length) {
      const rowPoints = pointPixels[index];
      for (const rowPoint of rowPoints) {
        ctx.fillRect(rowPoint[0], rowPoint[1], 3, 3);
      }
      delay = delay - 0.125;
      index++;
      fn();
    } else {
      const dom = document.getElementById('operation');
      dom.style.opacity = 1;
    }
  }, delay)
}
fn();
}


梦回童年的游戏菜单


之后我们来设计一下我们的操作界面,我们先去回顾一下那些老游戏的界面:


网络异常,图片无法展示
|


emm,看上去我们已经把上面的title做完了,要设计下面操作菜单了,我也是按照经典的来:


网络异常,图片无法展示
|


这里文字用了文字阴影效果。下面的单人游玩和多人游玩使用了金箍棒作为选择指针~我想金箍棒也是悟空的标志了。下面还要留下我的署名:Hancao present to Game Science in 2021.,是不是就有游戏厂商注册商标那感觉了~


<style>
#operation {
      position: absolute;
      left: 30vw;
      top: 55vh;
      width: 40vw;
      height: 300px;
      text-align: center;
      transition: all 2s;
      opacity: 0;
    }
    #title {
      color: white;
      font-size: 40px;
      font-weight: 800;
      text-shadow: 8px 8px 8px #888888;
      cursor: pointer;
      text-decoration: none;
    }
    .select {
      margin-top: 32px;
      text-align: left;
      padding-left: 160px;
    }
    .option {
      overflow: hidden;
    }
    .op {
      display: inline-block;
      overflow: hidden;
      line-height: 45px;
      font-size: 30px;
      font-weight: 600;
      text-shadow: 8px 8px 8px #888888;
      color: #fff;
    }
    .jingubang {
      display: inline-block;
      height: 20px;
      width: 8px;
      background-color: #555151;
      border-top: 10px solid #7c7469;
      border-bottom: 10px solid #7c7469;
      margin-right: 16px;
    }
    .placeholder {
      display: inline-block;
      height: 20px;
      width: 8px;
      margin-right: 16px;
    }
    .hancao {
      font-size: 8px;
      margin-top: 48px;
      color: #fff;
    }
</style>
<div id="operation">
    <a id="title" href="https://www.heishenhua.com">
      BLACKMYTH WUKONG
    </a>
    <div class="select">
      <div class="option">
        <div class="jingubang"></div>
        <div class="op">
          1 PLAYER
        </div>
      </div>
      <div class="option">
        <div class="placeholder"></div>
        <div class="op">
          2 PLAYERS
        </div>
      </div>
    </div>
    <div class="hancao">
      Hancao present to Game Science in 2021.
    </div>
</div>


完整代码在此,复制粘贴,与我一同重返童年


完整代码在此,大家可以复制粘贴,记得 vscode 下载live server插件来运行,否则,canvas存在图片代理问题


tip: 完整效果见头图。代码质量堪忧,首先是canvas不太会,而且我是边设计边编码的,所以代码结构没有经过设计~


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>wukong</title>
  <style>
    body {
      margin: 0;
      background-color: #000;
    }
    #my-canvas-wukong {
      position: absolute;
      margin-left: 50vw;
      margin-top: 20vh;
      width: 16vw;
      height: 32vh;
    }
    #my-canvas-monkey-king {
      position: absolute;
      margin-left: 30vw;
      margin-top: 20vh;
      width: 16vw;
      height: 32vh;
    }
    #operation {
      position: absolute;
      left: 30vw;
      top: 55vh;
      width: 40vw;
      height: 300px;
      text-align: center;
      transition: all 2s;
      opacity: 0;
    }
    #title {
      color: white;
      font-size: 40px;
      font-weight: 800;
      text-shadow: 8px 8px 8px #888888;
      cursor: pointer;
      text-decoration: none;
    }
    .select {
      margin-top: 32px;
      text-align: left;
      padding-left: 160px;
    }
    .option {
      overflow: hidden;
    }
    .op {
      display: inline-block;
      overflow: hidden;
      line-height: 45px;
      font-size: 30px;
      font-weight: 600;
      text-shadow: 8px 8px 8px #888888;
      color: #fff;
    }
    .jingubang {
      display: inline-block;
      height: 20px;
      width: 8px;
      background-color: #555151;
      border-top: 10px solid #7c7469;
      border-bottom: 10px solid #7c7469;
      margin-right: 16px;
    }
    .placeholder {
      display: inline-block;
      height: 20px;
      width: 8px;
      margin-right: 16px;
    }
    .hancao {
      font-size: 8px;
      margin-top: 48px;
      color: #fff;
    }
  </style>
</head>
<body>
  <canvas id="my-canvas-wukong">
  </canvas> 
  <canvas id="my-canvas-monkey-king">
  </canvas>
  <div id="operation">
    <a id="title" href="https://www.heishenhua.com">
      BLACKMYTH WUKONG
    </a>
    <div class="select">
      <div class="option">
        <div class="jingubang"></div> 
        <div class="op">
          1 PLAYER
        </div>
      </div>  
      <div class="option">
        <div class="placeholder"></div>
        <div class="op">
          2 PLAYERS
        </div>
      </div>  
    </div>
    <div class="hancao">
      Hancao present to Game Science in 2021.
    </div>
  </div>
  <script>
    setTimeout(() => {
      let canvas = document.getElementById("my-canvas-wukong");
    let ctx = canvas.getContext("2d");
    var image = new Image();
    image.src = "wukong.png";
    image.width = 240;
    image.height = 240;
    image.onload = function () {
      canvas.width = image.width;
      canvas.height = image.height;
      ctx.drawImage(image, 0, 0);
      var imageData = ctx.getImageData(0, 0, image.width, image.height).data;
      ctx.fillStyle = "#000";
      ctx.fillRect(0, 0, image.width, image.height);
      let pointPixels = [];
      let rowPoints = [];
      for (var h = image.height - 3; h >= 0; h -= 3) {
        if(h !== image.height - 3 ){
          pointPixels.push(rowPoints);
          rowPoints = [];
        }
        for (var w = image.width - 3; w >= 0; w -= 3) {
          var position = (image.width * h + w) * 4;
          var r = imageData[position], g = imageData[position + 1], b = imageData[position + 2];
          if (r + g + b !== 0) {
            rowPoints.push([w, h]);
          }
        }
      }
      ctx.fillStyle = "#fff";
      let index = 0;
      const length = pointPixels.length;
      let delay = 30;
      const fn = () => {
        let timer = setTimeout(() => {
          clearTimeout(timer);
          if(index != length){
            const rowPoints = pointPixels[index];
            for(const rowPoint of rowPoints){
              ctx.fillRect(rowPoint[0], rowPoint[1], 3, 3);
            }
            delay = delay - 0.125;
            index++;
            fn();
          } else {
            const dom = document.getElementById('operation');
            dom.style.opacity = 1;
          }
      }, delay)
      }
      fn();
    }
    // 显示孙悟空
    let canvas2 = document.getElementById("my-canvas-monkey-king");
            let ctx2 = canvas2.getContext("2d");
            var image2 = new Image();
            image2.src = "monkey-king.jpeg";
            image2.width = 700;
            image2.height = 700;
            image2.onload = function () {
              console.log(image2, canvas2, ctx2);
              canvas2.width = image2.width;
              canvas2.height = image2.height;
              ctx2.drawImage(image2, 0, 0);
              var imageData2 = ctx2.getImageData(0, 0, image2.width, image2.height).data;
              console.log(imageData2)
              ctx2.fillStyle = "#000";
              ctx2.fillRect(0, 0, image2.width, image2.height);
              var gap = 12;
              for (var h = 0; h < image2.height; h+=gap) {
                  for(var w = 0; w < image2.width; w+=gap){
                          var position = (image2.width * h + w) * 4;
                          var r = imageData2[position], g = imageData2[position + 1], b = imageData2[position + 2];
                          if(765 - (r + g + b) < 600) {
                            ctx2.fillStyle = `rgb(${r}, ${g}, ${b})`;
                            ctx2.fillRect(w,h,gap,gap);
                          }
                  }
              }
            }
    }, 5000);
  </script>
</body>
</html>


结束语 — 热爱,所以期待


网络异常,图片无法展示
|


首先,文章中关于游戏和前端的看法仅仅代表我的主观想法,欢迎评论区指正,以及感谢大帅老师的文章与代码对我有思路上的指引~


本篇文章到此就结束了,我们不需要去捧杀黑神话悟空,持续期待就好了。以及大家或许能从我的文章看出我们可以用技术去完成很多很多好玩的事情,我也希望我的存在可以为前端开发者带来更多的创造力,更多的idea,让大家对这个行业更热爱~


这可能就是我现在作为一个有趣的,有奇奇怪怪的点子的前端工程师可以为社区带来的东西吧。(当然我也有硬核内容,比如寒草的编译原理哈哈哈)

相关文章
|
3月前
|
前端开发 JavaScript
前端必看的8个HTML+CSS技巧 (六) 裁剪图像的动画
前端必看的8个HTML+CSS技巧 (六) 裁剪图像的动画
|
4月前
|
前端开发 JavaScript iOS开发
精选11款炫酷的前端动画特效分享(附在线演示)
分享11款非常不错炫酷的前端特效源码 其中包含css动画特效、js原生特效、svg特效等 下面我会给出特效样式图或演示效果图 但你也可以点击在线预览查看源码的最终展示效果及下载源码资源
|
4月前
|
人工智能 JavaScript 前端开发
【前端|JS实战第1篇】使用JS来实现属于自己的贪吃蛇游戏!
【前端|JS实战第1篇】使用JS来实现属于自己的贪吃蛇游戏!
|
6月前
|
存储 前端开发 JavaScript
前端实现俄罗斯方块游戏(内含源码)
前端实现俄罗斯方块游戏(内含源码)
80 2
|
1月前
|
前端开发 JavaScript UED
前端开发的魔法:CSS动画与JavaScript的完美结合
本文将探讨如何利用CSS动画和JavaScript的结合,为前端页面增添生动的效果。我们将通过实例展示如何使用这两种技术为网页元素创建吸引人的动画效果,并讨论它们的优缺点和适用场景。
29 0
|
2月前
|
前端开发 JavaScript API
前端开发中的动画效果优化技巧
在前端开发中,动画效果是提升用户体验的重要手段之一。本文将介绍一些优化动画效果的技巧,帮助开发者提升网页性能和用户体验。
|
4月前
|
前端开发 JavaScript 关系型数据库
前端毕业设计|基于Vue+Nodejs实现游戏资讯平台
前端毕业设计|基于Vue+Nodejs实现游戏资讯平台
|
4月前
|
前端开发 容器
【零基础入门前端系列】—动画和弹性盒模型(二十四)
【零基础入门前端系列】—动画和弹性盒模型(二十四)
|
5月前
|
移动开发 前端开发 JavaScript
前端开发中web和移动端动画的常见实现方式
前端动画一般在展示性网站、交互操作或者移动端活动页面使用比较多,可能对于大部分前端平时只会用 css 里的 transition 动画,其实前端动画还有很多实现方式
86 0
|
6月前
|
前端开发 数据可视化 定位技术
GIS前端-地图标绘与动画
GIS前端-地图标绘与动画
77 0