< template>
< div class = "btns" >
< VanButton size= "small" type= "primary" @click= "start" > 开始寻路< / VanButton>
< / div>
< canvas
id= "canvas"
width= "1000"
height= "600"
@click. left= "leftClick"
@click. right= "rightClick"
> < / canvas>
< / template>
< script setup lang= "ts" >
import { onMounted } from 'vue'
import DrawMap from './DrawMap.js'
import Astar from './AStar.js'
const gridSize = 20
let canvas : any
let drawMap : any
let astar : any
let maxX = 0
let maxY = 0
onMounted ( ( ) => {
canvas = document. querySelector ( '#canvas' )
maxX = Math. floor ( canvas. width / gridSize)
maxY = Math. floor ( canvas. height / gridSize)
drawMap = new DrawMap ( canvas, gridSize)
drawMap. init ( )
const obstacles = drawMap. dynamicFill ( 500 )
astar = new Astar ( maxX, maxY, obstacles)
} )
interface Click {
offsetX : number
offsetY : number
}
function leftClick ( e : Click) {
const x = Math. floor ( e. offsetX / gridSize)
const y = Math. floor ( e. offsetY / gridSize)
if ( x > 0 && x < maxX && y > 0 && y < maxY) {
drawMap. fill ( x, y, 'green' )
astar. setStartPoint ( x, y)
}
}
function rightClick ( e : Click) {
const x = Math. floor ( e. offsetX / gridSize)
const y = Math. floor ( e. offsetY / gridSize)
if ( x > 0 && x < maxX && y > 0 && y < maxY) {
drawMap. fill ( x, y, 'red' )
astar. setEndPoint ( x, y)
}
}
function start ( ) {
astar. findPath ( )
const path = astar. getFullpath ( )
for ( let i = 0 ; i < path. length; i++ ) {
const point = path[ i]
setTimeout ( ( ) => {
drawMap. fill ( point. x, point. y, 'green' )
} , 100 * i)
}
}
< / script>
< style lang= "less" scoped>
. btns {
height : 100px;
display : flex;
justify- content: center;
align- items: center;
}
#canvas {
background : #fff;
}
< / style>
class Point {
x = 0
y = 0
f = 0
g = 0
h = 0
parent = null
constructor ( x, y, parent = null ) {
this . x = x
this . y = y
this . parent = parent
}
}
export default class AStar {
openList = [ ]
closedList = [ ]
startPoint = null
endPoint = null
finalPoint = null
fullPath = [ ]
obstacles = [ ]
maxX = 0
maxY = 0
constructor ( maxX, maxY, obstacles = [ ] ) {
this . startPoint = new Point ( 0 , 0 )
this . endPoint = new Point ( 0 , 0 )
this . openList. push ( this . startPoint)
this . maxX = maxX
this . maxY = maxY
this . obstacles = obstacles
}
setStartPoint ( x, y ) {
this . startPoint = new Point ( x, y)
this . openList = [ this . startPoint]
}
setEndPoint ( x, y ) {
this . endPoint = new Point ( x, y)
}
findPath ( ) {
if ( this . openList. length === 0 ) {
console. log ( '死路' )
return
}
const current = this . openList[ 0 ]
if ( current. x === this . endPoint. x && current. y === this . endPoint. y) {
console. log ( '寻路已结束' )
this . finalPoint = current
return
}
const xl = current. x - 1
const yl = current. y
const flagl = xl > 0 && this . canAdd ( xl, yl)
if ( flagl) {
this . addOpenList ( xl, yl, current)
}
const xr = current. x + 1
const yr = current. y
const flagr = xr < this . maxX && this . canAdd ( xr, yr)
if ( flagr) {
this . addOpenList ( xr, yr, current)
}
const xt = current. x
const yt = current. y - 1
const flagt = yt > 0 && this . canAdd ( xt, yt)
if ( flagt) {
this . addOpenList ( xt, yt, current)
}
const xb = current. x
const yb = current. y + 1
const flagb = yb < this . maxY && this . canAdd ( xb, yb)
if ( flagb) {
this . addOpenList ( xb, yb, current)
}
this . openList. shift ( )
this . closedList. push ( current)
this . openList. sort ( ( a, b ) => a. f - b. f)
this . findPath ( )
}
addOpenList ( x, y, current ) {
const p = new Point ( x, y, current)
this . G ( p)
this . H ( p)
this . F ( p)
this . openList. push ( p)
}
canAdd ( x, y ) {
return ! (
this . openList. find ( item => item. x === x && item. y === y) ||
this . closedList. find ( item => item. x === x && item. y === y) ||
this . obstacles. find ( item => item. x === x && item. y === y)
)
}
getFullpath ( ) {
if ( this . finalPoint) {
this . fullPath. push ( {
x : this . finalPoint. x,
y : this . finalPoint. y,
} )
let parent = this . finalPoint. parent
while ( parent) {
this . fullPath. unshift ( {
x : parent. x,
y : parent. y,
} )
parent = parent. parent
}
}
return this . fullPath
}
F ( p ) {
p. f = p. g + p. h
}
G ( p ) {
p. g = p. parent. g + 1
}
H ( p ) {
p. h = Math. sqrt (
Math. pow ( this . endPoint. x - p. x, 2 ) + Math. pow ( this . endPoint. y - p. y, 2 ) ,
) . toFixed ( 2 )
}
}
export default class DrawMap {
ctx = null
width = 0
height = 0
gridSize = 20
colorStep = 5
constructor ( canvas, gridSize ) {
this . ctx = canvas. getContext ( '2d' )
this . width = canvas. width
this . height = canvas. height
this . gridSize = gridSize
}
init ( ) {
this . drawGrid ( )
}
drawGrid ( ) {
this . ctx. beginPath ( )
for ( let i = 0 ; i < this . width; i += this . gridSize) {
this . ctx. moveTo ( i, 0 )
this . ctx. lineTo ( i, this . height)
}
for ( let i = 0 ; i < this . height; i += this . gridSize) {
this . ctx. moveTo ( 0 , i)
this . ctx. lineTo ( this . width, i)
}
this . ctx. strokeStyle = '#ccc'
this . ctx. stroke ( )
}
fill ( x, y, color = '#000' ) {
this . ctx. beginPath ( )
this . ctx. rect (
x * this . gridSize,
y * this . gridSize,
this . gridSize,
this . gridSize,
)
this . ctx. fillStyle = color
this . ctx. fill ( )
}
dynamicFill ( num = 100 ) {
this . ctx. clearRect ( 0 , 0 , this . width, this . height)
this . drawGrid ( )
const arr = [ ]
for ( let i = 0 ; i < num; i++ ) {
const x = Math. floor ( Math. random ( ) * ( this . width / this . gridSize) )
const y = Math. floor ( Math. random ( ) * ( this . height / this . gridSize) )
const index = arr. findIndex ( item => item. x === x && item. y === y)
if ( index === - 1 ) {
arr. push ( { x, y } )
this . fill ( x, y)
}
}
return arr
}
}