canvas 移动端元素按画的路径行走,类大富翁游戏demo

                                     

最后效果,下面代码是初始demo,逻辑都在

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
		<link rel="stylesheet" href="./css/index.css">
	</head>
	<body>
		<body>
			<div class="page">
				<canvas id="canvas"></canvas>
				<div class="footer">
					<div class="btn-box">
						<div class="btn1">
							摇一摇
						</div>
					</div>
				</div>
			</div>
			<script src="./js/path.js"></script>
			<script src="./js/canvas.js"></script>
			<script src="./js/index.js"></script>
		</body>
	</body>
</html>
* {
	margin: 0;
	padding: 0;
}

.page {
	width: 100vw;
	height: 100vh;
	position: relative;
}

#canvas {
	position: absolute;
	left: 0;
	top: 0;
	z-index: 1;
	background-image: url(../image/bg.png);
	background-repeat: no-repeat;
	background-size: 100% 100%;
}

.footer {
	position: absolute;
	bottom: 0;
	height: 100px;
	z-index: 2;
}
.header{
	width: 100%;
	position: absolute;
	top: 0;
	height: 200px;
	z-index: 2;
}
.header img{
	width: 100%;
	height: 100%;
}
.btn1 {
	font-size: 50px;
}

 canvas.js

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// 节点大小
let nodeW = 15;
let nodeH = 8;
// 走过的路
let walkPath = 1;
let preyWalkPath = 1;
var totalTime = 500; // 运动总时间,单位为毫秒
var currentTime = 0; // 当前已经运动的时间,单位为毫秒
// 动画是否执行
let animateKg = true
// 图片路径数组
var imagePaths = [{
		name: "car",
		path: "./image/d1.png"
	},
	{
		name: "node",
		path: "./image/3.png"
	},
	{
		name: "nodeA",
		path: "./image/4.png"
	},
];
let allImage = [];
// 获取设备像素比
var devicePixelRatio = window.devicePixelRatio || 1;
// 获取视口(viewport)的宽度和高度
var viewportWidth = document.documentElement.clientWidth;
var viewportHeight = document.documentElement.clientHeight;
// 设置Canvas元素的实际宽度和高度
canvas.width = viewportWidth * devicePixelRatio;
canvas.height = viewportHeight * devicePixelRatio;
canvas.style.width = "100%";
canvas.style.height = "100%";
let scaleX = viewportWidth / (375 * devicePixelRatio);
let scaleY = viewportHeight / (670 * devicePixelRatio);
var path = null;
path = JSON.parse(JSON.stringify(map1));
// 路径适配
path.forEach((item) => {
	item.ex = item.ex * scaleX;
	item.ey = item.ey * scaleY;
	item.mx = item.mx * scaleX;
	item.my = item.my * scaleY;
	item.lx1 = item.lx1 * scaleX;
	item.ly1 = item.ly1 * scaleY;
	item.lx2 = item.lx2 * scaleX;
	item.ly2 = item.ly2 * scaleY;
});
// 加载图片并返回 Promise 对象
function loadImage(image) {
	return new Promise(function(resolve, reject) {
		var img = new Image();
		img.onload = function() {
			resolve({
				name: image.name,
				image: img
			});
		};
		img.onerror = function() {
			console.log(3);
			reject(new Error("Failed to load image: " + image.path));
		};
		img.src = image.path;
	});
}
// 创建多个图层
var layer1 = createLayer();
var layer2 = createLayer();
// 路径宽度
layer1.ctx.lineWidth = 3;
layer2.ctx.lineWidth = 3;
// 预加载所有图片
Promise.all(imagePaths.map(loadImage))
	.then(function(images) {
		// 图片加载完成后进行绘制操作
		console.log(1);
		allImage = images;
		updata();
	})
	.catch(function(error) {
		console.log(2);
		console.error(error);
	});
// 创建图层的函数
function createLayer() {
	var layerCanvas = document.createElement("canvas");
	layerCanvas.width = viewportWidth * devicePixelRatio;
	layerCanvas.height = viewportHeight * devicePixelRatio;
	layerCanvas.style.width = viewportWidth + "px";
	layerCanvas.style.height = viewportHeight + "px";
	var layerContext = layerCanvas.getContext("2d");
	layerContext.scale(devicePixelRatio, devicePixelRatio);
	return {
		canvas: layerCanvas,
		ctx: layerContext,
	};
}
// 二次贝塞尔曲线函数
function quadraticBezierCurve(t, p0, p1, p2) {
	// 计算当前位置
	var currentBezierX =
		(1 - t) * (1 - t) * p0.x + 2 * t * (1 - t) * p1.x + t * t * p2.x;
	var currentBezierY =
		(1 - t) * (1 - t) * p0.y + 2 * t * (1 - t) * p1.y + t * t * p2.y;
	return {
		x: currentBezierX,
		y: currentBezierY,
	};
}
// 绘制路径
function drawRoute(ex, ey, mx, my, lx1, ly1, lx2, ly2, index, kg, layer) {
	if (index) {
		// 曲线
		layer.ctx.beginPath();
		layer.ctx.moveTo(mx, my);
		layer.ctx.globalCompositeOperation = "destination-over";
		layer.ctx.quadraticCurveTo(lx1, ly1, lx2, ly2);
		layer.ctx.stroke();
		layer.ctx.closePath();
	}
	// 节点
	layer.ctx.beginPath();
	layer.ctx.globalCompositeOperation = "source-over";
	let img = kg ? allImage[1].image : allImage[2].image;
	layer.ctx.drawImage(img, ex - nodeW / 2, ey - nodeH / 2, nodeW, nodeH);
	layer.ctx.fill();
	layer.ctx.closePath();
}
// 绘制小车
function drawCar(x, y) {
	// 在图像加载完成后绘制图像
	layer2.ctx.globalCompositeOperation = "source-over";
	layer2.ctx.drawImage(allImage[0].image, x - 20, y - 40, 40, 40);
}
// 遍历路径
function getPath(data, kg, layer) {
	data.forEach((item, index) => {
		drawRoute(
			item.ex,
			item.ey,
			item.mx,
			item.my,
			item.lx1,
			item.ly1,
			item.lx2,
			item.ly2,
			index,
			kg,
			layer
		);
	});
}
// 绘制所有路径
function allPath() {
	layer1.ctx.fillStyle = "white";
	layer1.ctx.strokeStyle = "yellow";
	getPath(path, true, layer1);
}

function overPath(index) {
	layer2.ctx.globalCompositeOperation = "destination-over";
	layer2.ctx.fillStyle = "red"; // 设置填充颜色
	layer2.ctx.strokeStyle = "red"; // 设置边框颜色
	getPath(path.slice(0, index), false, layer2);
}
// 绘制页面
function drawPage() {
	ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除上一次的绘制
	layer1.ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除上一次的绘制
	layer2.ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除上一次的绘制
	allPath();
	// 绘制走过路径
	let pI = preyWalkPath - 1 ? preyWalkPath - 1 : 1;
	overPath(pI);
	if (currentTime >= totalTime - 16) {
		overPath(preyWalkPath);
	}
	// 绘制车
	var t = currentTime / totalTime;
	// 计算贝塞尔曲线上的点
	const point = quadraticBezierCurve(
		t, {
			x: path[preyWalkPath - 1].mx,
			y: path[preyWalkPath - 1].my,
		}, {
			x: path[preyWalkPath - 1].lx1,
			y: path[preyWalkPath - 1].ly1,
		}, {
			x: path[preyWalkPath - 1].lx2,
			y: path[preyWalkPath - 1].ly2,
		}
	);
	drawCar(point.x, point.y);
	ctx.drawImage(layer1.canvas, 0, 0);
	ctx.drawImage(layer2.canvas, 0, 0);
}

function updata() {
	drawPage();
	// 请求下一帧动画
	// 更新当前时间
	currentTime += 16; // 假设每帧间隔为16毫秒
	// 判断是否继续动画
	if (currentTime < totalTime) {
		requestAnimationFrame(updata);
	} else {
		if (preyWalkPath < walkPath) {
			currentTime = 0;
			requestAnimationFrame(updata);
			preyWalkPath++;
		} else {
			// 动画结束
			animateKg = true
		}
	}
}

index.js

let btn1=document.querySelector('.btn1')
btn1.onclick=()=>{
	if(animateKg){
		animateKg=false
		let random=Math.floor(Math.random()*2)+1;
		preyWalkPath=walkPath+1
		walkPath+=random;
		currentTime=0;
		updata()
	}
}

path.js


let map1 = [{
		ex: 100,
		ey: 500,
		mx: 100,
		my: 500,
		lx1: 100,
		ly1: 500,
		lx2: 100,
		ly2: 500
	},
	{
		ex: 140,
		ey: 510,
		mx: 100,
		my: 500,
		lx1: 120,
		ly1: 515,
		lx2: 140,
		ly2: 508
	},
	{
		ex: 180,
		ey: 510,
		mx: 140,
		my: 510,
		lx1: 160,
		ly1: 515,
		lx2: 180,
		ly2: 508
	},
	{
		ex: 220,
		ey: 500,
		mx: 180,
		my: 508,
		lx1: 200,
		ly1: 510,
		lx2: 220,
		ly2: 498
	},
	{
		ex: 260,
		ey: 470,
		mx: 220,
		my: 500,
		lx1: 260,
		ly1: 500,
		lx2: 260,
		ly2: 470
	},
	{
		ex: 220,
		ey: 450,
		mx: 260,
		my: 470,
		lx1: 240,
		ly1: 440,
		lx2: 220,
		ly2: 452
	},
	{
		ex: 180,
		ey: 430,
		mx: 220,
		my: 450,
		lx1: 200,
		ly1: 500,
		lx2: 180,
		ly2: 430
	},
	
	{
		ex: 230,
		ey: 400,
		mx: 180,
		my: 430,
		lx1: 190,
		ly1: 350,
		lx2: 230,
		ly2: 400
	}
]

文件列表: (源代码+详细注释)大富翁 .......................\Document .......................\........\allclasses-frame.html .......................\........\allclasses-noframe.html .......................\........\CardCanvas.html .......................\........\constant-values.html .......................\........\Controlor.html .......................\........\deprecated-list.html .......................\........\DiceCanvas.html .......................\........\GameMenu.html .......................\........\help-doc.html .......................\........\HelpForm.html .......................\........\HighScoreCanvas.html .......................\........\index-all.html .......................\........\KMRichMan.html .......................\........\OpenCanvas.html .......................\........\OpenCanvasTimerTask.html .......................\........\OptionList.html .......................\........\overview-tree.html .......................\........\packages.html .......................\........\PlayCanvas.html .......................\........\PlayerStatusForm.html .......................\........\PlayMessageForm.html .......................\........\serialized-form.html .......................\........\StockForm.html .......................\........\StockList.html .......................\........\stylesheet.css .......................\........\SystemList.html .......................\........\点这里啊.html .......................\Game .......................\....\rich.jar .......................\....\storage .......................\....\.......\music1.msc .......................\....\.......\music3.msc .......................\....\.......\music4.msc .......................\....\.......\music6.msc .......................\....\.......\RS@1.db .......................\Readme.txt .......................\SourceCode .......................\..........\CardCanvas.java .......................\..........\Controlor.java .......................\..........\DiceCanvas.java .......................\..........\GameMenu.java .......................\..........\HelpForm.java .......................\..........\HighScoreCanvas.java .......................\..........\KMRichMan.java .......................\..........\OpenCanvas.java .......................\..........\OpenCanvasTimerTask.java .......................\..........\OptionList.java .......................\..........\PlayCanvas.java .......................\..........\PlayCanvas.java.bak .......................\..........\PlayerStatusForm.java .......................\..........\PlayMessageForm.java .......................\..........\res .......................\..........\...\image .......................\..........\...\.....\barbw.png .......................\..........\...\.....\card0.png .......................\..........\...\.....\card1.png .......................\..........\...\.....\card2.png .......................\..........\...\.....\card3.png .......................\..........\...\.....\card4.png .......................\..........\...\.....\card5.png .......................\..........\...\.....\card6.png .......................\..........\...\.....\card7.png .......................\..........\...\.....\card8.png .......................\..........\...\.....\card9.png .......................\..........\...\.....\gamepanelbw.png .......................\..........\...\.....\housebw.png .......................\..........\...\.....\logobw.png .......................\..........\...\.....\logobw2.png .......................\..........\...\.....\lost.png .......................\..........\...\.....\map0bw.png .......................\..........\...\.....\map1bw.png .......................\..........\...\.....\start00bw.png .......................\..........\...\.....\win.png .......................\..........\StockForm.java .......................\..........\StockList.java .......................\..........\SystemList.java
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值