Vue 项目 购物车 模块

购物车 ajax 接口请求拆分到 service 文件夹中

// goods.js
import axios from 'axios'

export default {
    getGoodsInfo () {
        return axios.get('/api/goods')
        .then(res => {
            // 处理数据格式,并返回
            const {code, data: goodsInfo, slider, keys} = res.data;
            if (code) {
                return {goodsInfo, slider, keys};
            } else {
                return null;
            }
        });
    }
}

路由切换动态

// 1. 使用 transition 组件 嵌套 router-view 组件
<transition name="route-move">
    <router-view class="child-view" />
</transition>

// 2. 通过 CSS 定义路由切换动画
.route-move-enter {
    // 入场前状态
    transform: translate3d(-100%, 0, 0);
}
.route-move-leave-to{
    // 离场后状态
    transform: translate3d(100%, 0, 0);
}
.route-move-enter-active,
.route-move-leave-active{
    // 激活状态
    transition: transform 0.3s;
}

购物车动画,使用 Vue 的 JS 方式的半场动画

// CartAnim.vue
<template>
<div class="ball-wrap">
    <transition
        @before-enter="beforeEnter"
        @enter="enter"
        @after-enter="afterEnter"
    >
        <div class="ball" v-show="show">
            <div class="inner">
                <div class="cubeic-add"></div>
            </div>
        </div>
    </transition>
</div>
</template>

<script>
export default {
    name: "cartAnim",
    data () {
        return {
            show: false
        }
    },
    methods: {
        start(el) {
            // 启动动画接口,传递点击按钮元素
            this.el = el;
            // 适应.ball显示,激活动画钩子
            this.show = true;
        },
        beforeEnter(el) {
            // 把小球移动到点击的DOM元素所在位置
            const rect = this.el.getBoundingClientRect();
            // 转换为用于绝对定位的坐标
            const x = rect.left - window.innerWidth /2;
            const y = -(window.innerHeight - rect.top - 10 - 20);
            // ball 只移动 y 轴
            el.style.transfrom = `translate3d(0, ${y}px, 0)`;
            // inner 只移动 x 轴
            const inner = el.querySelector(".inner");
            inner.style.transform = `translate3d(${x}px, 0, 0)`;
        },
        enter(el, done) {
            // 获取offsetHeight就会重绘
            document.body.offsetHeight;
            //指定动画结束位置
            el.style.transform = `translate3d(0, 0, 0)`;
            const inner = el.querySelector(".inner");
            inner.style.transform = `translate3d(0, 0, 0)`;
            el.addEventListener("transitionend", done);
        },
        afterEnter(el) {
            // 动画结束, 开始清理工作
            this.show = false;
            el.style.display = "none";
            this.$emit("transitionend");
        }
    }
}
</script>

<style scoped>
.ball-wrap .ball{
    position: fixed;
    left: 50%;
    bottom: 10px;
    z-index: 100000;
    color: red;
    transition: all .5s cubic-bezier(0.49, -0.29, 0.75, 0.41);
}
.ball-wrap .ball .inner{
    width: 16px;
    height: 16px;
    transition: all 0.5s linear;
    background: #f00;
    border-radius: 50%;
}
.ball-wrap .ball .inner .cubeic-add {
    font-size: 22px;
}
</style>

// CartAnim 組件的使用
// 1. 引入组件
import CartAnim from '@/components/CartAnim.vue';
// 2. 挂载到组件选项中
components: {
    CartAnim
}
// 3. 在组件模板中 使用组件,并在商品列表组件上监听启动事件
<cart-anim ref="ca"></cart-anim>
<good-list @cartanim="$refs.ca.start($event)" />

// 4. 在 good-list 组件中通过 $emit 派发自定义事件
this.$emit('cartanim', event.target);

全局创建组件实例函数

import Vue from 'vue';

export default function(Component, props) {
    // 创建vue实例
    const instance = new Vue({
        render: h => h(Component, { props });
    }).$mount();
    
    // 把生成的dom追加至body中
    document.body.appendChild(instance.$el);
    
    // 添加一个销毁方法
    const comp = instance.$children[0];
    comp.remove = function () {
        document.body.removeChild(instance.$el);
        instance.$destroy();
    }
    return comp;
}

// 在入口 main.js 中注入方法
import create from './utils/create'
Vue.prototype.$create = create;

// 在组件中使用 $create 方法
const anim = this.$create(CartAnim);
anim.start(el);
anim.$on("transitionend", anim.remove);
上一篇:Color the ball(差分数组)


下一篇:canvas核心技术-如何实现复杂的动画