废了又废话

上篇文章发了之后,有挺多朋友来注册线上地址的,受宠若惊。

然后看了看上篇文章的日期,6个月前,emmmm,为什么我这么懒。。。

开了一个交流群,532413727,大家有啥想问的想说的可以加群交流。

第三回(卡牌攻击)

接着上节所说,接下来制作卡牌的ui,首先创建一个component文件夹,在文件夹中创建我们的Card.vue,按照上一章我们的设计图:

写出Card的简略dom如下:

<template>
    <div class="card">
        <div class="card-name">{{name}}</div>
        <div class="card-cost">
            {{cost}}
        </div>

        <div class="card-content">{{content}}</div>

        <div class="card-bottom">
            <div>
                <div class="attack">{{attack}}</div>
            </div>
            <div>
                <div class="life">{{life}}</div>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    name: "Card",
    data () {
        return {
            name: "测试",
            cost: 5,
            content: "测试卡片",
            attack: 10,
            life: 10
        }
    }
}
</script>

这个时候可以在GameTable.vue中引入这个组件进行一下预览,在my-card-area区域中去掉之前的{{count}},加入刚刚Card组件,记得要在script里面引入Card。这个时候就能看到一个非常丑陋的卡片了。

这是因为我们还没添加组件的样式,我们为组件加上基本的样式:

<style>
.card {
    position: relative;
    width: 135px;
    height: 170px;
    font-size: 12px;
    border: 1px solid #ccc;
    border-radius: 5px;
    background: white;
}

.card-name {
    height: 45px;
    background: #394950;
    color: white;
    border-top-right-radius: 5px;
    border-top-left-radius: 5px;
    text-align: center;
    margin-bottom: 15px;
    box-sizing: border-box;
    padding: 8px 0;
    font-weight: bold;
}

.card-cost {
    position: absolute;
    background-color: white;
    color: #394950;
    border-radius: 50%;
    width: 25px;
    height: 25px;
    left: 4px;
    top: 33px;
    display: flex;
    justify-content: center;
    align-items: center;
    box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08);
}

.card-content {
    padding: 0 8px;
}

.card-bottom {
    position: absolute;
    bottom: 0px;
    width: 100%;
    background: #394950;
    display: flex;
    justify-content: space-between;
    padding: 8px 10px;
    color: white;
    box-sizing: border-box;
    border-bottom-right-radius: 5px;
    border-bottom-left-radius: 5px;
}
</style>

添加了这些样式之后,就能看到我们想要的卡片效果了,当然,你也可以按照自己的想法修改上面的样式,卡片的样式不会影响游戏的功能。

这个时候给对手的桌面上也放上一张卡片,也就是在other-card-area中也增加一个Card。

<div class="table">
    <div class="other-card-area">
        <Card />
    </div>
    <div class="my-card-area">
        <Card />
    </div>
</div>

双方都有卡片了,现在要制作攻击效果了。

攻击的效果如果使用dom来制作,会非常的不方便,所以需要使用到canvas来绘制这部分的效果,我的思路是先将动画制作出来,然后再应用到卡片上,然后再实现攻击的逻辑部分,那么开始动手。

首先在页面上添加canvas,编写样式和context初始化:

HTML:

<canvas id="animationCanvas" v-show="showCanvas" :width="windowWidth" :height="windowHeight"></canvas>

Javascript(GameTable文件,mounted方法的尾部):

this.windowWidth = window.innerWidth;
this.windowHeight = window.innerHeight;

window.onresize = () => {
    this.windowWidth = window.innerWidth;
    this.windowHeight = window.innerHeight;
}
this.canvasContext = document.querySelector("#animationCanvas").getContext("2d");

CSS:

#animationCanvas {
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    z-index: 999;
}

我们需要的效果是鼠标按下后,在页面上画一根线条,松开鼠标时线条消失,所以思路很简单,在按下鼠标时记录按下的点A,在鼠标移动的时候绘制点A到当前位置的一根线条,在松开鼠标的时候清除线条,所以添加下面的代码到mounted代码尾部:

window.onmousedown = (e) => {
    window.isAttackDrag = true;
    this.attackStartX = e.pageX;
    this.attackStartY = e.pageY;
}

window.onmousemove = (e) => {
    if (window.isAttackDrag) {
        window.requestAnimationFrame(() => {
            this.canvasContext.clearRect(0, 0, this.windowWidth, this.windowHeight);
            this.canvasContext.strokeStyle = 'maroon';
            this.canvasContext.fillStyle = 'maroon';


            this.canvasContext.save();
            this.canvasContext.setLineDash([40, 10]);
            this.canvasContext.lineWidth = 30;

            this.canvasContext.beginPath();
            this.canvasContext.moveTo(this.attackStartX, this.attackStartY);
            this.canvasContext.lineTo(e.pageX, e.pageY);
            this.canvasContext.fill();
            this.canvasContext.stroke();
            this.canvasContext.restore();
        })
    }
}

window.onmouseup = () => {
    if (window.isAttackDrag) {
        window.isAttackDrag = false;
        this.canvasContext.clearRect(0, 0, this.windowWidth, this.windowHeight)
    }
}

保存后查看效果,恩~还不错,不过总觉得少了点什么,哦!是箭头!

那么尝试画一个箭头在顶端吧,其实就是画一个三角形在顶端,绘制三角形其实很简单,但是难点就是这根直线是有角度的,所以三角形也要跟着直线的角度旋转。好在这些都是在网上能查到资料的,所以把下面这段代码加到刚刚绘制直线的代码后面,就能看到效果了:

this.canvasContext.save();
this.canvasContext.beginPath();
this.canvasContext.lineCap = 'square';
this.canvasContext.translate(e.pageX, e.pageY);
let getLineRadians = () => {
    let _a = e.pageX - this.attackStartX;
    let _b = e.pageY - this.attackStartY;
    let _c = Math.hypot(_a, _b);
    return Math.acos(_a / _c) * Math.sign(_b);
};
this.canvasContext.rotate(getLineRadians() - Math.PI /2);
this.canvasContext.moveTo(35, -40);
this.canvasContext.lineTo(0, 25);
this.canvasContext.lineTo(-35, -40);
this.canvasContext.lineTo(35, -40);
this.canvasContext.fill();
this.canvasContext.stroke();
this.canvasContext.restore();

到这里,实现了第一步,但是目前在任意位置都能绘制这样一根直线,而我们的目标是只有在我们的卡牌上才可以进行攻击操作,所以要修改一下我们刚刚的代码。

首先将原来的window.mousedown事件注释掉,将mousedown事件绑定到Card组件中,在Card的最外层的div上加入mouseDown事件,在事件里emit事件到外层:

mouseDown(e) {
    this.$emit('onAttackStart', {
        startX: e.pageX, startY: e.pageY
    })
}

外层中GameTable页面写一个onAttackStart的方法来接收事件,这个事件其实就是之前的mousedown事件,只是多了一个控制canvas是否显示的变量:

onAttackStart({startX, startY}) {
    this.showCanvas = true;
    window.isAttackDrag = true;
    this.attackStartX = startX;
    this.attackStartY = startY;
}

有显示canvas就要能隐藏,在之前的onmouseup方法里,加入this.showCanvas = false

与此同时,在GameTable组件中的my-card-area里的Card组件,传入刚刚写好的onAttackStart,这样就实现了只能在Card上面拖拽进行攻击了。

下一回我计划做卡牌攻击的效果和服务端的一些事情,感兴趣的同学请继续留意吧。

github地址在这里,这篇文章的代码tag是a-3。


发表评论

电子邮件地址不会被公开。 必填项已用*标注