这个厉害了,用前端重返童年🥤
共 26237字,需浏览 53分钟
·
2021-09-24 23:46
点击上方关注 前端技术江湖,一起学习,天天进步
前言 — 背景介绍
国产 3A 游戏情怀
了解我的朋友都知道,我是一个很喜欢玩游戏的人,我甚至还六一儿童节发表过儿童节杂谈游戏记忆——《生化危机5》[1],小时候玩的游戏也不少,从插卡游戏机到ps3,ps4也都有经历过。
我们这些玩着国外3A游戏长大的孩子一直有一个小小的愿望(其实是大大的梦想),就是玩到属于我们的国产3A游戏,并且不仅仅是由国人开发,国人制作,而是包含着中国的文化底蕴,我们有着五千年深厚的历史文化,我们的游戏应该做到文化输出,让世界的游戏玩家看到中国的游戏,中国的文化,中国的魅力。
大家会不会觉得上面的话很熟悉,没错,国产动画同理,小时候看着日漫美漫长大的孩子们一直也是在期待着国产动画崛起,突然那一年,大圣归来,身穿虎虎生风黄金甲
,身披炙热烈焰火红袍
,手抡天河定底神珍铁
。为国产动漫电影打开了新的道路,但是后面的大圣归来游戏不尽如人意,然而去年游戏科学
出了黑神话悟空
的预告,今年又出了虚幻5引擎的新版预告。看得我热血沸腾,上次是大圣,这是又是大圣,悟空要开辟我热爱的游戏产业的国产3A新道路了。「此处因为寒草🌿 太过热血沸腾,于是有了这篇文章,和这几百行代码」
玩游戏的孩子长大了,成为了工程师
时间回到现在,没错,寒草看完演示视频久久不能平静的现在,现在的寒草🌿 褪去稚气,成为了一个在前端行业摸爬滚打一年的工程师,今天中午和朋友去吃好吃的(没错,周末一定要有好吃的啊~),和他我说了很多黑神话悟空的消息,我越说越激动,心想,那我用我的技术,来表达我那一份对国产游戏的期待与对游戏对前端行业的热爱吧。
为大家带来有趣的前端内容,用前端去做更好玩的事情,一直是我的初心。一路以来,感谢有你们☀️
正篇 — 用前端重返童年
我之前没有用过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>
复制代码
结束语 — 热爱,所以期待
首先,文章中关于游戏和前端的看法仅仅代表我的主观想法,欢迎评论区指正,以及感谢大帅老师[2]的文章与代码对我有思路上的指引~
本篇文章到此就结束了,我们不需要去捧杀黑神话悟空,持续期待就好了。以及大家或许能从我的文章看出我们可以用技术去完成很多很多好玩的事情,我也希望我的存在可以为前端开发者带来更多的创造力,更多的idea,让大家对这个行业更热爱~
这可能就是我现在作为一个有趣的,有奇奇怪怪的点子的前端工程师可以为社区带来的东西吧。(当然我也有硬核内容,比如寒草的编译原理哈哈哈)
最后,热爱,所以期待
踏过三界宝刹,阅过四洲繁华。
笑过五蕴痴缠,舍过六根牵挂。
为情义披战甲,为爱人挑担、牵马、送晚霞。
怕什么欲念不休,怕什么浪迹天涯。
步履不停,便是得救之法。
——敢问路在何方?路在脚下。
伙伴们,如果喜欢我的文章,可以点赞 👍 关注➕ ,这是对我最大的支持。
加我微信:hancao97,邀你进群,了解寒草🌿 的github小组现状,一起学习前端,成为更优秀的工程师~(群二维码在这里->前端晚晚睡[3], 二维码过期了的话看链接沸点中的评论,我会把最新的二维码放在评论区,当然也可以加我微信我拉你进群,毕竟我也是有趣的前端,认识我也不赖🌟~)
关于本文
作者:寒草
https://juejin.cn/post/6999413377692860430
The End
欢迎自荐投稿到《前端技术江湖》,如果你觉得这篇内容对你挺有启发,记得点个 「在看」哦
点个『在看』支持下