因为在网络上浏览信息时,总会看到一些网页(博客园居多)会带有一个小女孩的样式,鼠标移入不同位置会显示不同的文字,这个东西很有趣。经过查阅,发现这种技术叫做live2D,它在某些类型的游戏中使用很多。提起游戏我又想起来,有一款叫做碧蓝航线的游戏,它的人物可以通过触摸来出发音效,不过我没有玩过,只是在视频里面见过。因为我最近做了几个关于canvas的玩具,所以我准备来试试使用canvas来复刻一下碧蓝航线里面的那种效果。不过,这里需要先指出,我的标题已经明确了,这是一个粗制滥造的复刻,哈哈,具体效果,仁者见仁智者见智吧。
一、视频演示
这是我的B站视频,视频的噪声较大(我都笔记本已经老了),建议佩戴耳机进行观看,效果会好一点。
强烈推荐你观看演示视频,毕竟这里涉及到了声音,这是无法使用文字进行表达的信息。
canvas实现碧蓝航线语音(粗制滥造)
二、实现思路
1.几何体近似
对于一个具体的人物,我们需要将它切分成几个部分,针对某一个部分进行判断。那如何切分呢?一个简单的想法是使用集合体进行近似。作为一个理工男,这就需要发挥我们的抽象思维了。下面放一张图片来展示我所理解的抽象,这张图片应该很好理解的,我就不做具体说明了。针对具体的图片使用这些几何体进行近似,基本上就是这样了。
2.逻辑判断
这里涉及到逻辑判断的问题,具体就是如何判断鼠标点击到了哪一个部位呢?这就需要我们前面的几何体近视了,几何体其实就是表示的某一个部位。当鼠标点击时,依次绘制上述的几何体,绘制完成一个之后,就进行判断是否鼠标点击的点在该几何体内部,如果在的话,就触发相应的音效。
三、代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>抽象版</title>
<style>
body, div {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="cas" style="margin: 0 auto;">
<canvas id="cs" width="600" height="700" style="border: 2px solid green"></canvas>
</div>
<script type="text/javascript">
'use strict'
let canvas = document.getElementById("cs");
// 获取canvas的宽高
let width = canvas.width;
let height = canvas.height;
let ctx = canvas.getContext("2d");
// 设置背景完全透明
ctx.strokeStyle = "red";
ctx.strokeStyle = "rgba(255, 255, 255, 0)";
let current = -1;
let rewidth = 600; // 重定义图片的宽度
let imgs = [];
// 创建img元素
Array.from({length: 1}, (a, i)=>i).forEach(index => {
let img = document.createElement("img")
img.src = "test.png";
// 当图片加载完成之后,将其绘制到canvas上,图片没有加载完成时,
// 图片的宽高是0,因为这时候图片其实是空的
img.onload = () => {
let h = resize(img, rewidth);
img.width = rewidth;
img.height = h;
drawCanvas(img);
};
});
function drawHeader(x, y) {
ctx.beginPath();
ctx.rect(245, 20, 78, 88);
ctx.stroke();
if (!ctx.isPointInPath(x, y)) {
return ;
}
console.log("点击了头");
playAudio("./1.mp3");
}
function drawNeck(x, y) {
ctx.beginPath();
ctx.rect(270, 110, 30, 21);
ctx.stroke();
if (!ctx.isPointInPath(x, y)) {
return ;
}
console.log("点击了脖子");
playAudio("./2.mp3");
}
function drawUpperHalf(x, y) {
ctx.beginPath();
ctx.rect(245, 133, 80, 165);
ctx.stroke();
if (!ctx.isPointInPath(x, y)) {
return ;
}
console.log("点击了上半身");
playAudio("./3.mp3");
}
function drawLegs(x, y) {
ctx.beginPath();
ctx.rect(228, 300, 45, 368);
ctx.rect(292, 300, 45, 368);
ctx.stroke();
if (!ctx.isPointInPath(x, y)) {
return ;
}
console.log("点击了腿");
playAudio("./4.mp3");
}
function drawArms(x, y) {
ctx.beginPath();
ctx.rotate(40*Math.PI/180);
ctx.rect(250, -40, 30, 220);
ctx.rotate(280*Math.PI/180);
ctx.rect(160, 320, 30, 170);
ctx.rotate(40*Math.PI/180);
ctx.stroke();
if (!ctx.isPointInPath(x, y)) {
return ;
}
console.log("点击了胳膊");
playAudio("./5.mp3");
}
function drawPlate(x, y) {
ctx.beginPath();
ctx.moveTo(516, 322);
ctx.arc(465, 322, 51, 0, Math.PI*2);
ctx.stroke();
if (!ctx.isPointInPath(x, y)) {
return ;
}
console.log("点击了盘子");
playAudio("./6.mp3");
}
function playAudio(path) {
let url = path;
let audio = new Audio(url)
audio.addEventListener("canplaythrough", event => {
audio.play();
});
}
// 添加鼠标事情,来实现图片的拖放功能。
// 当鼠标按下时,获取当前的坐标
canvas.onmousedown = e => {
console.log("鼠标按下")
let x = e.pageX-canvas.offsetLeft; // 后面这个是偏移量,但是在这里为0
let y = e.pageY-canvas.offsetTop;
console.log(x + " -> " + y);
drawHeader(x, y); // 绘制头部
drawNeck(x, y); // 绘制脖子
drawUpperHalf(x, y); // 绘制上半身
drawLegs(x, y); // 绘制腿
drawArms(x, y); // 绘制胳膊
drawPlate(x, y); // 绘制盘子
};
function drawCanvas(img) {
ctx.drawImage(img, 0, 0, img.width, img.height);
}
// 根据传如的指定宽度,自动调整高度
function resize(img, width) {
let w = img.width;
let h = img.height;
return parseInt(h*width/w);
}
</script>
</body>
</html>
四、补充
上面这种几何体进行近似实际上面临很多不方便之处,因为很多人物图片都不是这样正面的。所以其实符合的图片很难找,这就是图片需要符合程序了。但是,应当是程序符合图片才对,所以我又想了其它的方法,我觉得可以使用曲线进行近似。具体的做法就是使用鼠标,在图片上面进行绘制,然后拿到曲线的坐标,每次绘制曲线的坐标,不再是几何体了,这样做理论上可以适合机会所有的图片了。不过我还没有进行尝试,目前仅仅是一个不成熟的想法。