Vue.js项目实战-多语种网站(租车)
首先来看一下网站效果,想写这个项目的读者可以自行下载哦,地址:https://github.com/Stray-Kite/Car:




在这个项目中,我们主要是为了学习语种切换,也就是右上角的 中文/English 功能的实现。
首先看一下模拟的后台数据src/config/modules/lang.js 文件中:


关键代码:

export default {
name: 'Homepage',
components: {
ScrollNumber
},
data () {
return {
lang: 'chinese',
pageIndex: 0,
stepIndex: 0
}
},
......其余代码 methods: {
addClass (el, _class) { //切换语言
toggleLang (lang) {
this.lang = lang
// this.animatePage()
}
},
//以下几个computed是获取config文件夹里的数据
computed: {
langs () {
return config.langs[this.lang] //主要靠这里切换,这个切换的本质其实就是使用了另一套英文的数据(换了一套后台数据)
},
......其余代码 }
}
最后给大家一波儿带注解的代码,方便理解:
Homepage.vue(CSS我就不给啦,因为这东西看着学一下就好):
<template>
<div class="container"> <div class="rc-header">
<!--首先这是两个切换语言的按钮(中文/English)-->
<div class="rc-header-right">
<a href="#" :class="{'is-active': this.lang === 'chinese'}" @click="toggleLang('chinese')">中文</a> /
<a href="#" :class="{'is-active': this.lang === 'english'}" @click="toggleLang('english')">English</a>
</div>
<!--顶栏其他的元素,图片等...-->
<div class="rc-header-left">
<img class="rc-logo" :src="media.LOGO_PATH">
<img class="rc-slogan" :src="media.SLOGAN_PATH">
</div>
</div> <!--下面就是整个内容页了,大的来说这四页就是一个大的竖着的轮播图,用了vue的swiper-->
<div class="swiper-container rc-body">
<div class="swiper-wrapper"> <!--第一页-->
<div class="swiper-slide">
<div class="rc-page rc-page01">
<!--星空和车-->
<div class="rc-galaxy">
<div class="rc-bg rc-layer-bg" ref="plbo"
:style="{backgroundImage: 'url(' + common.GALAXY_LAYER_BG + ')', top: layerEdge, bottom: layerEdge, left: layerEdge, right: layerEdge}">
</div>
<div class="rc-bg rc-layer-top" ref="plto"
:style="{backgroundImage: 'url(' + common.GALAXY_LAYER_TOP + ')'}">
</div>
</div> <div class="rc-content-wrapper">
<!--若是第一页,即index=0,则显示,否则隐藏-->
<div class="rc-text-wrapper" ref="ptwo"
:style="{visibility: pageIndex === 0 ? 'visible' : 'hidden'}" >
<h2>{{langs.PAGE_ONE_SLOGAN}}</h2>
<!--scroll-number是数字增加,引入widgets/ScrollNumber.vue的方法-->
<scroll-number :size="isMobile ? 18 : 24" ref="psno">
<span slot="suffix" style="color: #c3c3c3;">{{langs.PAGE_ONE_SUB_SLOGAN}}</span>
</scroll-number>
</div>
<button class="btn btn-success btn-try"
:style="{visibility: pageIndex === 0 ? 'visible' : 'hidden'}"
ref="pbro">{{langs.TRY_LABEL}}</button>
</div> <!--下面这两个img不用管,已经隐藏啦-->
<img :src="common.GALAXY_REAL_TOP" v-show="false">
<img :src="common.GALAXY_REAL_BG" v-show="false">
</div>
</div> <!--第二页-->
<div class="swiper-slide">
<div class="rc-page rc-page02">
<div class="rc-content-wrapper">
<div
class="rc-text-wrapper"
:style="{visibility: pageIndex === 1 ? 'visible' : 'hidden'}"
ref="ptwt">
<h2>{{langs.PAGE_TWO_SLOGAN}}</h2>
</div>
<div class="rc-step-wrapper">
<div class="rc-step-stage">
<div class="swiper-container rc-step-container">
<div class="swiper-wrapper">
<div
v-for="(step, index) in langs.STEPS_PROFILE"
:key="index"
class="swiper-slide rc-step">
<div class="rc-step-content">
<h2 class="rc-step-title">{{langs.STEP_LABEL}} {{index + 1}}</h2>
<p class="rc-step-profile">{{step}}</p>
<button
class="btn"
ref="pbdt"
v-if="index === 0">{{langs.DOWNLOAD_LABEL}}</button>
<button
class="btn"
ref="pbat"
v-if="index === 1">{{langs.AUTH_LABEL}}</button>
</div>
</div>
</div>
<div class="swiper-scrollbar"></div>
<div class="swiper-button-next" v-if="!isMobile"></div>
<div class="swiper-button-prev" v-if="!isMobile"></div>
</div>
</div>
<!--步骤右面的图片-->
<div class="rc-step-scene" v-if="!isMobile">
<div
:style="{backgroundImage: 'url(' + media.STEPS_ACTOR_PATH[stepIndex] + ')'}"
class="rc-bg rc-step-actor"></div>
</div>
</div>
</div>
</div>
</div> <!--第三页-->
<div class="swiper-slide">
<div class="rc-page rc-page03">
<div class="rc-content-wrapper">
<div
class="rc-text-wrapper"
:style="{visibility: pageIndex === 2 ? 'visible' : 'hidden'}"
ref="ptwth">
<h2>{{langs.PAGE_THREE_SLOGAN}}</h2>
</div>
<div class="rc-cars-gallery">
<div
v-for="(car, index) in langs.CARS_PROFILE"
:key="index"
class="rc-car">
<div class="rc-car-card" :title="langs.CARS_TOOLTIP">
<div class="rc-car-media">
<div
:style="{backgroundImage: 'url(' + common.CARS_PATH[index] + ')'}"
class="rc-bg rc-bg-scale rc-car-snapshot"></div>
</div>
<div class="rc-car-profile">
<h4>{{car.NAME}}</h4>
<div>{{langs.OWNER_LABEL}}: {{car.OWNER}}</div>
<div>{{langs.DATE_LABEL}}: {{car.DATE}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div> <!--第四页-->
<div class="swiper-slide">
<div class="rc-page rc-page04">
<div
class="rc-text-wrapper"
:style="{visibility: pageIndex === 3 ? 'visible' : 'hidden'}"
ref="ptwf">
<h2>{{langs.PAGE_FOUR_SLOGAN}}</h2>
</div>
<div class="rc-content-wrapper">
<div class="rc-bg rc-drive-stage">
<div
:style="{backgroundImage: 'url(' + common.DRIVE_STAGE_ACTOR + ')'}"
class="rc-bg rc-actor-car" ref="pdcf"></div>
</div>
</div>
</div>
</div>
</div>
<div class="swiper-pagination" v-show="!isMobile"></div>
</div>
</div>
</template> <script>
/* eslint-disable */
import Swiper from 'swiper'
import 'swiper/dist/css/swiper.min.css'
import 'animate.css/animate.min.css'
import config from '../config'
import ScrollNumber from '@/widgets/ScrollNumber.vue'
export default {
name: 'Homepage',
components: {
ScrollNumber
},
data () {
return {
lang: 'chinese',
pageIndex: 0,
stepIndex: 0
}
},
beforeCreate () {
/**
* 旋转星空
* step 3: 计算可视区域对角线, 根据对角线长度撑开容器
*/
let w = document.body.clientWidth
let h = document.body.clientHeight
this.layerEdge = 'calc(50% - ' + Math.ceil(Math.sqrt(w * w + h * h) / 2) + 'px)'
},
mounted () {
this.$nextTick(function () {
let _self = this
/* eslint-disable no-new */
new Swiper('.rc-body', {
speed: 800,
direction: 'vertical',
paginationClickable: true,
mousewheel: true,
pagination: {
el: '.swiper-pagination'
},
on: {
slideChangeTransitionEnd () {
_self.pageIndex = this.activeIndex
_self.animatePage()
}
}
})
this.initPage()
})
},
methods: {
addClass (el, _class) {
let elClassArr = el.className.split(' ') //用单个空格分割
let classArr = _class.split(' ') //同上
classArr.forEach(item => {
// 如果elClassArr中没有这个元素(类型),那么添加进去
if (elClassArr.indexOf(item) === -1) {
elClassArr.push(String(item))
}
})
//添加完后本属性后面要有一个空格隔开
el.className = elClassArr.join(' ')
return el
},
removeClass (el, _class) {
let elClassArr = el.className.split(' ')
let classArr = _class.split(' ')
classArr.forEach(item => {
let index = elClassArr.indexOf(item)
if (index > -1) {
elClassArr.splice(index, 1)
}
})
el.className = elClassArr.join(' ')
return el
},
bindAnimation (el, x) {
let _self = this
// 监听动画结束事件
let events = [
'webkitAnimationEnd',
'mozAnimationEnd',
'MSAnimationEnd',
'oanimationend',
'animationend'
]
_self.addClass(el, x + ' animated')
events.forEach(event => {
let func = function () {
events.forEach(item => {
el.removeEventListener(item, func)
})
_self.removeClass(el, x + ' animated')
}
//增加事件监听,如果时间结束,那么再利用fun函数移除监听的事件和添加进来的新类(class属性)
el.addEventListener(event, func)
})
},
initPage01 () {
setTimeout(() => {
/**
* 旋转星空
* step 4: 采用真实图片替换代理图片
*/
this.$refs.plto.style.backgroundImage = 'url(' + this.common.GALAXY_REAL_TOP + ')'
this.$refs.plbo.style.backgroundImage = 'url(' + this.common.GALAXY_REAL_BG + ')'
}, 2000)
this.animatePage01() //动画效果(数字上升)
},
//第二页
initPage02 () {
let _self = this
let pbdt = this.$refs.pbdt[0]
let pbat = this.$refs.pbat[0]
let bindAnimation = this.bindAnimation
new Swiper('.rc-step-container', {
speed: 600,
scrollbar: {
el: '.swiper-scrollbar',
draggable: true
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
},
on: {
slideChangeTransitionEnd () {
if (this.activeIndex === 0) {
bindAnimation(pbdt, 'tada')
} else if (this.activeIndex === 1) {
bindAnimation(pbat, 'tada')
}
_self.stepIndex = this.activeIndex
}
}
})
},
initPage03 () {},
initPage04 () {}, //初始化四个界面方法
initPage () {
this.initPage01()
this.initPage02()
this.initPage03()
this.initPage04()
}, animatePage01 () {
this.bindAnimation(this.$refs.ptwo, 'fadeIn')
this.bindAnimation(this.$refs.pbro, 'fadeIn')
this.$refs.psno.$emit('start')
},
animatePage02 () {
this.bindAnimation(this.$refs.ptwt, 'fadeInDown')
},
animatePage03 () {
this.bindAnimation(this.$refs.ptwth, 'flipInY')
},
animatePage04 () {
this.bindAnimation(this.$refs.ptwf, 'jackInTheBox')
setTimeout(() => {
this.bindAnimation(this.$refs.pdcf, 'rc-arrive')
}, 600)
},
// QA: 函数本是对象
animatePage (index) {
[
this.animatePage01,
this.animatePage02,
this.animatePage03,
this.animatePage04
][index || this.pageIndex]()
},
//切换语言
toggleLang (lang) {
this.lang = lang
// this.animatePage()
}
},
//以下几个computed是获取config文件夹里的数据
computed: {
langs () {
return config.langs[this.lang]
},
media () {
return config.media[this.lang]
},
common () {
return config.common
},
// 判断移动端,无需记住,会用就好,返回值为bool值
isMobile () {
return 这个就不给了,这个直接用就好了 }
}
}
</script> <style scoped>
</style>
widgets/ScrollNumber.vue(数字增长):
<template>
<div class="scroll-number" :style="{fontSize: size + 'px'}">
<slot name="prefix"></slot>
<span class="number">{{value}}</span>
<slot name="suffix"></slot>
</div>
</template> <script>
import TWEEN from 'tween.js'
export default {
name: 'ScrollNumber',
data () {
return {
value: 0,
animation: null
}
},
props: {
total: {
type: Number,
default: 15374
},
size: {
type: Number,
default: 24
}
},
mounted () {
this.$nextTick(function () {
const animate = function (time) {
requestAnimationFrame(animate)
TWEEN.update(time)
}
requestAnimationFrame(animate)
let _self = this
// 增长起始值,从0开始
let surface = {value: 0}
// 增加从0增长到total动画1600为时间
this.animation = new TWEEN.Tween(surface).to({value: _self.total}, 1600)
this.animation.easing(TWEEN.Easing.Exponential.Out).onUpdate(() => {
_self.value = Math.floor(surface.value)
})
// 监听父组件通讯事件
this.$on('start', () => {
surface.value = 0
this.animation.start()
})
})
}
}
</script> <style scoped>
.number {
display: inline-block;
min-width: 40px;
color: orangered;
text-align: center;
font-weight: 600;
}
</style>
注:其中TWEEN的用法我找到了一篇比较不错的文章,地址:https://www.jianshu.com/p/164538a89939
Vue.js项目实战-多语种网站(租车)的更多相关文章
- 第10章-Vue.js 项目实战
一.本节内容 掌握项目环境中路由的配置方法 ***** 熟练掌握编写单文件组件的编写 *** 能够使用swiper.js进行轮播图组件的封装 能够使用axios进行数据请求 二.webpack项目的目 ...
- Vue.js项目实战-打造线上商城
首先上一下完成后的效果: 首页: 商品详情页: 购物车页(其实还有个订单页,只是和购物车页基本类似,所以就不截图啦): 开始项目: 由于涉及的是前后端分离,所以我们的后台数据就模拟存储于浏览器端(co ...
- 新书上线:《Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统》,欢迎大家买回去垫椅子垫桌脚
新书上线 大家好,笔者的新书<Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统>已上线,此书内容充实.材质优良,乃家中必备垫桌脚 ...
- Vuejs实例-01使用vue-cli脚手架搭建Vue.js项目
[TOC] 1. 前言 vue-cli 一个简单的构建Vue.js项目的命令行界面 整体过程: $ npm install -g vue-cli $ vue init webpack vue-admi ...
- vue.js项目构建
这里构建的vue.js项目依赖node服务器运行. 项目搭建完整步骤: 安装node.js ,转至nodeJs网站http://nodejs.cn/ 下载nodeJs进行安装. 安装完毕检查nodeJ ...
- 【Vue.js游戏机实战】- Vue.js实现九宫格水果机抽奖游戏总结
大家好!先上图看看本次案例的整体效果. 完整版实战课程附源码:[Vue.js游戏机实战]- Vue.js实现九宫格水果机抽奖 实现思路: Vue component实现九宫格水果机组件,可以嵌套到任意 ...
- 【Vue.js游戏机实战】- Vue.js实现老虎-机抽奖总结
大家好!先上图看看本次案例的整体效果. 完整版实战课程附源码:[Vue.js游戏机实战]- Vue.js实现老虎-机抽奖 实现思路: Vue component实现老虎-机组件,可以嵌套到任意要使用的 ...
- 如何将你的 Vue.js 项目部署在云开发静态托管之上
云开发静态托管是云开发提供的静态网站托管的能力,静态资源(HTML.CSS.JavaScript.字体等)的分发由腾讯云对象存储 COS 和拥有多个边缘网点的腾讯云 CDN 提供支持. 在云开发静态托 ...
- vue.js项目安装
Vue.js 安装 NPM 方法安装vue.js项目 npm 版本需要大于 3.0,如果低于此版本需要升级它: # 查看版本 $ npm -v 2.3.0 #升级 npm npm install np ...
随机推荐
- MySQL基于报错注入2
目标站点: 0x1 注入点判断 http://www.xxxxxx.com/pages/services.php?id=1 #true http://www.xxxxxx.com/pages/serv ...
- vue 地图可视化 maptalks 篇
Maptalks 项目是一个 HTML5 的地图引擎, 基于原生 ES6 Javascript 开发: - 二三维一体化地图, 通过二维地图的旋转 /倾斜增加三维视角 - 插件化设计, 能与其他图形库 ...
- MD5 加密工具类MD5Util
我们在使用MD5 在线加密的时候,会发现下面情况,大小写的区别就不说啦,那么16位和32位有啥区别呢,其实16 位实际上是从 32 位字符串中,取中间的第 9 位到第 24 位的部分,就是str.su ...
- MySQL事务和锁——《MySQL DBA工作笔记》
MySQL事务 事务存在的原因 事务存在的目的:保证用户对数据操作对数据是安全的.(比如说银行卡余额) 事务的特性--ACID 原子性:一个事务要么全部执行,要么不执行 一致性:事务开始和结束时,数据 ...
- c++ 多态的内幕
c++ 多态,就是利用了一个二级指针(指针数组),数组里的每个元素都指向了,用virtual修饰的成员函数. 既然提到了指针,那就让我们用内存地址来证明一下吧. 为了证明,我们必须要取到成员函数的首地 ...
- JAVA学习方法之——费曼学习法
理查德·费曼 费曼简介 理查德·菲利普斯·费曼(Richard Phillips Feynman),出生于1918年5月11日,是美籍犹太裔物理学家,曾在1965年获得诺贝尔物理学奖,也被认为是继爱因 ...
- golang数据结构和算法之StackArray数组堆栈
用数组实现的堆栈, 另一种,是用链表实现的堆栈, 在各种不同的编程语言上, 实现都是类似的. StackArray.go package StackArray //基于数组实现的堆栈 const ar ...
- python-判断文件后缀名
>>> str = 'jidlhdpf.jpg' >>> str.endswith('.jpg') True endswith
- (day54)六、事务、分组、F、Q、常用字段、事务
目录 一.聚合查询aggregate 二.分组查询annotate 三.F与Q查询 (一)F查询 1. 查询库存数大于卖出数的书籍 2. 将所有书的价格上涨100块 3.将所有书的名称后面全部加上 & ...
- Docker 简单发布dotnet core项目 文本版
原文:https://www.cnblogs.com/chuankang/p/9474591.html docker发布dotnet core简单流程 照着步骤来基本没错 但是有几个要注意的地方: v ...