목차
초기 프로젝트
项目初始化
项目中使用到的mixin
配置路径别名
移动端300毫秒延时和点透问题
对jsonp进一步封装
vue的生命周期函数
better-scroll组件的使用
开发模式下的请求代理
负外边距的作用效果
配置子路由
Vuex的使用
Vuex是什么?
Vuex怎么用?
Vuex란 무엇인가요?
Vuex를 어떻게 사용하나요?
简单的多组件数据交互
vuex稍微复杂点的使用
js中给CSS添加prefix
移动端的touch事件
播放器内核开发
audio标签
进度条组件
svg实现圆形进度条
全屏和退出全屏
歌词页的显示
Vue中的mixin
节流处理
animation动画
封装localStorage操作
路由懒加载
웹 프론트엔드 JS 튜토리얼 Vue 기반의 모바일 웹 뮤직 플레이어 구현

Vue 기반의 모바일 웹 뮤직 플레이어 구현

Jul 14, 2018 am 11:12 AM
html javascript vue.js vuex

이 글은 주로 vue를 기반으로 한 모바일 웹 뮤직 플레이어의 구현을 소개합니다. 이제는 모든 사람과 공유합니다. 도움이 필요한 친구들은 이를 참고할 수 있습니다.

초기 프로젝트

프로젝트 GitHub 주소: https:/ / github.com/bjw1234/vu...

프로젝트 시연 주소: http://music.baijiawei.top

이 프로젝트를 배우는 데 너무 오랜 시간이 걸려서 드디어 완성되었습니다. 프로젝트가 좋다고 생각하시면 별표를 눌러주세요. star

长文预警...
^_^||

项目初始化

1

2

3

4

5

// 安装vue脚手架工具

npm install vue-cli -g

 

// 初始化webpack应用

vue init webpack vue-music

로그인 후 복사

项目中使用到的mixin

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

// 背景图片

bg-image($url)

  background-image: url($url "@2x.png")

  @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3)

    background-image: url($url "@3x.png")

 

// 不换行

no-wrap()

  text-overflow: ellipsis

  overflow: hidden

  white-space: nowrap

 

// 扩展点击区域

extend-click()

  position: relative

  &:before

    content: ''

    position: absolute

    top: -10px

    left: -10px

    right: -10px

    bottom: -10px

로그인 후 복사

配置路径别名

1

2

3

4

5

6

7

resolve: {

    extensions: ['.js''.vue''.json'],

    alias: {

      '@': resolve('src'),

      'common': resolve('src/common')

    }

}

로그인 후 복사

移动端300毫秒延时和点透问题

fastclick:处理移动端click事件300毫秒延迟和点透问题。

先执行安装fastclick的命令。

1

npm install fastclick --save

로그인 후 복사

之后,在main.js中引入,并绑定到body

1

2

3

import FastClick from 'fastclick';

 

FastClick.attach(document.body);

로그인 후 복사

注意: fastclick和其他的模块点击冲突,导致点击事件不可用时,可以给对应的dom添加needsclick类来解决。

对jsonp进一步封装

下载原始的jsonp模块:

1

npm install jsonp --save

로그인 후 복사

再次封装:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

import originJSONP from 'jsonp';

 

/**

 * 做一个简单的jsonp封装

 * @param url

 * @param data

 * @param option

 * @return {Promise}

 */

export default function jsonp (url, data, option) {

  return new Promise((resolve, reject) => {

    url = `${url}?${_obj2String(data)}`;

    originJSONP(url, option, (err, data) => {

      if (!err) {

        resolve(data);

      else {

        reject(err);

      }

    });

  });

};

 

function _obj2String (obj, arr = [], index = 0) {

  for (let item in obj) {

    arr[index++] = [item, obj[item]];

  }

  return new URLSearchParams(arr).toString();

}

로그인 후 복사

vue的生命周期函数

Vue 기반의 모바일 웹 뮤직 플레이어 구현

注意: 当使用keep-alive组件时,当切换到其他路由,会调用前组件的deactivated钩子函数,当切回来时,会调用activated函数。

better-scroll组件的使用

注意:

  • 1.better-scroll只处理容器的第一个子元素的滚动。

  • 2.一定得保证子元素超出父元素,这样才能正确的滚动。

初始化:

1

2

3

4

5

6

import BScroll from 'better-scroll';

 

let wrapper = document.querySelector('.wrapper');

let scroll = new BScroll(wrapper,{

    // 配置项

});

로그인 후 복사

1

2

3

4

5

6

7

8

.wrapper

    position: fixed

    width: 100%

    top: 88px

    bottom: 0

    .scroll

        height: 100%

        overflow: hidden

로그인 후 복사

问题排查(无法滚动原因:)

  • 1.内层容器的高度没有超过外层容器。

  • 2.dom没有渲染完毕就初始化better-scroll

  • 3.改变了dom的显隐性,没有对scroll进行重新计算。

  • 针对3:当dom显示出来之后,加20毫秒延时,然后调用refresh方法。

开发模式下的请求代理

当在开发模式下,需要使用一些后台接口,为了防止跨域问题,vue-cli提供了非常强大的http-proxy-middleware包。可以对我们的请求进行代理。
进入 config/index.js 代码下如下配置即可:

1

2

3

4

5

6

7

8

9

10

proxyTable: {

  '/getDescList': {

    target: 'http://127.0.0.1:7070/desclist', // 后端接口地址

    changeOrigin: true,

    // secure: false,

    pathRewrite: {

      '^/getDescList''/'

    }

  }

}

로그인 후 복사

负外边距的作用效果

  • marin-left或者margin-top是负值:它会将元素在相应的方向进行移动。left就是左右方向移动,top就是上下方向移动。也就是会使元素在文档流里的位置发生变化

  • margin-right或者margin-bottom是负值:它不会移动该元素(该元素不变化),但会使该元素后面的元素往前移动。也就是说,如果margin-bottom为负值,那么该元素下面的元素会往上移动;如果margin-right为负值,那么该元素右边的元素会往左移动,从而覆盖该元素。

配置子路由

需求:在歌手页面下需要一个歌手详情页。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

export default new Router({

    routes:[

        {

            path: '/',

            component: Singer,

            children: [

                {

                    path: ':id',

                    compoonent: SingerDetail

                }

            ]

        },

        ...

    ]

});

로그인 후 복사

当监听到用户点击之后进行路由跳转:

1

2

3

4

5

6

this.$router.push({

  path: `singer/${singer.id}`

});

 

// 别忘了在`Singer`页面中:

<router-view></router-view>

로그인 후 복사

Vuex的使用

Vuex是什么?

简单来说:Vuex解决项目中多个组件之间的数据通信和状态管理。

Vuex将状态管理单独拎出来,应用统一的方式进行处理,采用单向数据流的方式来管理数据。用处负责触发动作(Action)进而改变对应状态(State),从而反映到视图(View)上。

Vue 기반의 모바일 웹 뮤직 플레이어 구현

Vuex怎么用?

安装:

1

npm install vuex --save

로그인 후 복사

引入:

1

2

3

4

import Vuex from 'vuex';

import Vue from 'Vue';

 

Vue.use(Vuex);

로그인 후 복사

Vuex的组成部分

使用Vuex开发的应用结构应该是这样的:

Vue 기반의 모바일 웹 뮤직 플레이어 구현

  • State

State负责存储整个应用的状态数据,一般需要在使用的时候在根节点注入store对象,后期就可以使用this.$store.state直接获取状态。

1

2

3

4

5

6

7

8

import store from './store';

..

 

new Vue({

    el: '#app',

    store,

    render: h => h(App)

});

로그인 후 복사

那么这个store

긴 기사 경고...
^_^||
프로젝트 초기화🎜

1

2

3

4

5

6

7

8

9

10

const mutations = {...};

const actions = {...};

const state = {...};

 

// 实例化store对象并导出

export defautl new Vuex.Store({

    state,

    actions,

    mutations

});

로그인 후 복사
로그인 후 복사
🎜프로젝트에 사용된 Mixin🎜

1

2

3

4

5

6

7

8

const mutations = {

    changState(state) {

        // 在这里改变state中的数据

    }

};

 

// 可以在组件中这样触发

this.$store.commit('changeState');

로그인 후 복사
로그인 후 복사
🎜구성 경로 별칭🎜

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

import Vue from 'vue';

import Vuex from 'vuex';

 

Vue.use(Vuex);

 

// 状态

const state = {

  singer: {}

};

 

// 跟踪状态的变化

const mutations = {

  setSinger (state, singer) {

    state.singer = singer;

  }

};

 

// 实例化store对象

export default new Vuex.Store({

  state,

  mutations

});

 

// 在singer组件中提交数据

this.$store.commit('setSinger',singer);

 

// 在singer-detail组件中接收数据

let singer = this.$store.state.singer;

로그인 후 복사
로그인 후 복사
🎜모바일 단말기의 경우 300밀리초 지연 및 클릭연결 문제 🎜🎜fastclick: 모바일 클릭 이벤트의 300밀리초 지연 및 클릭연결 문제를 처리합니다. 🎜🎜먼저 명령을 실행하여 fastclick을 설치하세요. 🎜

1

2

3

4

5

const sate = {

    singer: {}    

};

 

export default state;

로그인 후 복사
로그인 후 복사
🎜 이후, main.js에 도입하고 body🎜

1

export const SET_SINGER = 'SET_SINGER';

로그인 후 복사
로그인 후 복사
🎜에 바인딩하세요. 참고: fastclick이 다른 모듈 클릭과 충돌하면 클릭 이벤트를 사용할 수 없게 됩니다. , 해당 DOM에 needsclick 클래스를 추가하여 문제를 해결할 수 있습니다. 🎜🎜jsonp의 추가 캡슐화🎜🎜원본 jsonp 모듈 다운로드: 🎜

1

2

3

4

5

6

7

8

9

10

import * as types from './mutation-types';

 

// 通过这个函数可以传入payload信息

const mutations = {

    [types.SET_SINGER](state,singer){

        state.singer = singer;

    }

};

 

export default mutations;

로그인 후 복사
로그인 후 복사
🎜다시 캡슐화: 🎜

1

export const singer = state => state.singer;

로그인 후 복사
로그인 후 복사
🎜vue의 수명 주기 기능🎜🎜Vue 기반의 모바일 웹 뮤직 플레이어 구현🎜🎜 참고: 언제 언제 keep-alive 구성 요소를 사용하면 다른 경로로 전환할 때 이전 구성 요소의 deactivated 후크 기능이 호출되어 다시 전환할 때 활성화됩니다. > 함수가 호출됩니다. 🎜🎜better-scroll 구성 요소 사용🎜🎜 참고: 🎜
  • 🎜1.better-scroll은 컨테이너의 첫 번째 하위 요소만 처리합니다. 스크롤의. 🎜
  • 🎜2. 올바르게 스크롤할 수 있도록 하위 요소가 상위 요소를 초과하는지 확인하세요. 🎜
🎜초기화: 🎜

1

// 暂时没有什么异步操作

로그인 후 복사
로그인 후 복사

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

// 入口文件

import Vue from 'vue';

import Vuex from 'vuex';

import state from './state';

import mutations from './mutations';

import * as actions from './actions';

import * as getters from './getters';

import createLogger from 'vuex/dist/logger';

 

Vue.use(Vuex);

 

// 调试环境下开启严格模式

const debug = process.env.NODE_ENV !== 'production';

 

// 创建store对象并导出

export default new Vuex.Store({

  state,

  actions,

  getters,

  mutations,

  strict: debug,

  plugins: debug ? [createLogger()] : []

});

로그인 후 복사
로그인 후 복사
🎜문제 해결(스크롤 실패 이유:)🎜
  • 🎜1. 외부 컨테이너 레이어 컨테이너를 초과하지 마십시오. 🎜
  • 🎜2.dom은 렌더링이 완료되기 전에 better-scroll 초기화됩니다. 🎜
  • 🎜3. 스크롤을 다시 계산하지 않고 dom의 명시성과 열성을 변경했습니다. 🎜
  • 🎜3: dom이 표시된 후 20밀리초 지연을 추가한 다음 refresh 메서드를 호출하세요. 🎜
🎜개발 모드에서 프록시 요청🎜🎜개발 모드에서는 도메인 간 문제를 방지하기 위해 일부 백엔드 인터페이스를 사용해야 합니다. vue-cli는 매우 강력한 http-proxy-middleware 패키지입니다. 우리의 요청은 프록시될 수 있습니다.
config/index.js를 입력하고 다음 코드를 구성합니다: 🎜

1

2

// main.js中引入

import store from './store';

로그인 후 복사
로그인 후 복사
🎜음수 ​​여백의 효과🎜
  • 🎜 marin-left 또는 margin-top은 음수 값입니다. 요소를 해당 방향으로 이동합니다. 왼쪽은 왼쪽과 오른쪽 방향으로 움직이는 것을 의미하고, top은 위아래 방향으로 움직이는 것을 의미합니다. 즉, 문서 흐름에서 요소의 위치가 변경됩니다. 🎜
  • 🎜margin-right 또는 margin-bottom은 음수 값입니다. 요소를 이동하지는 않지만(요소는 변경되지 않음) 이 요소 뒤에 오는 요소가 앞으로 이동하게 됩니다. 즉, margin-bottom이 음수인 경우 요소 아래의 요소는 margin-right가 음수인 경우 위로 이동합니다. 그러면 요소 오른쪽에 있는 요소가 왼쪽으로 이동하여 요소를 덮게 됩니다. 🎜
🎜하위 경로 구성🎜🎜요구 사항: 가수 페이지 아래에 가수 세부 정보 페이지가 필요합니다. 🎜

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// 使用这个语法糖

import { mapMutations } from 'vuex';

 

methods:{

    ...mapMutations({

        // 将这个函数(setSinger)和mutations中用于修改状态的函数关联起来

        setSinger: 'SET_SINGER'

    });

}

 

// 传参

this.setSinger(singer);

 

// 语法糖的本质

  this.$store.commit('setSinger', singer);

로그인 후 복사
로그인 후 복사
🎜사용자의 클릭을 모니터링할 때 라우팅 점프를 수행합니다. 🎜

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

import { mapGetters } from 'vuex';

 

export default {

    // 使用一个计算属性

    computed: {

        ...mapGetters([

            'singer'   // 这个就是getters.js中的那个singer

        ]);

    },

    created(){

        console.log(this.singer);

    }    

}

 

// 语法糖的本质:

let singer = this.$store.state.singer;

로그인 후 복사
로그인 후 복사
🎜Vuex 사용🎜

Vuex란 무엇인가요?

🎜간단히 말하면 Vuex는 프로젝트의 여러 구성 요소 간의 데이터 통신 및 상태 관리를 해결합니다. 🎜🎜Vuex는 상태 관리를 분리하여 통일된 방식으로 처리하며 단방향 데이터 흐름을 사용하여 데이터를 관리합니다. 목적은 작업(Action)을 트리거한 다음 해당 상태(State)를 변경하여 이를 뷰(View)에 반영하는 역할을 합니다. ). 🎜🎜Vue 기반의 모바일 웹 뮤직 플레이어 구현 🎜

Vuex를 어떻게 사용하나요?

🎜 설치: 🎜

1

2

3

this.$refs.image.style.transform = `scale(${scale})`;

this.$refs.image.style.webkitTansform = `scale(${scale})`;

...

로그인 후 복사
로그인 후 복사
🎜 소개: 🎜

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

let elementStyle = document.createElement('p').style;

 

// 得到合适的浏览器前缀

let vendor = (() => {

  let transformNames = {

    webkit: 'webkitTransform',

    Moz: 'MozTransform',

    O: 'OTransform',

    ms: 'msTransform',

    standard: 'transform'

  };

 

  for (let key in transformNames) {

    let support = elementStyle[transformNames[key]] !== undefined;

    if (support) {

      return key;

    }

  }

  return false;

})();

 

// 对外暴露的方法

export function prefixStyle (style) {

  if (vendor === false) {

    return style;

  }

  if (vendor === 'standard') {

    return style;

  }

  let result = vendor + style.charAt(0).toUpperCase() + style.substr(1);

  return result;

}

로그인 후 복사
로그인 후 복사
🎜 Vuex 구성 요소 🎜🎜 Vuex를 사용하여 개발된 애플리케이션 구조는 다음과 같아야 합니다. 이것 : 🎜🎜 32094947-5b21c94947-5b21c94947- .png🎜
  • 🎜🎜
🎜가 책임을 집니다. 전체 애플리케이션의 상태 데이터를 저장하려면 일반적으로 store 객체를 사용할 때 루트 노드에 삽입해야 합니다. this.$store.state를 사용하면 됩니다. 나중에 상태를 직접 가져옵니다. 🎜

1

2

3

4

5

6

7

8

// 导入该模块

import { prefixStyle } from 'common/js/dom';

 

// 加了合适前缀的CSS属性

const TRANSFORM = prefixStyle('transform');

 

// 使用该CSS属性

this.$refs.image.style[TRANSFORM] = `scale(${scale})`;

로그인 후 복사
로그인 후 복사
🎜그럼 이 상점은 뭔가요? 그것은 어디에서 왔습니까? 🎜

store可以理解为一个容器,包含应用中的state。实例化生成store的过程是:

1

2

3

4

5

6

7

8

9

10

const mutations = {...};

const actions = {...};

const state = {...};

 

// 实例化store对象并导出

export defautl new Vuex.Store({

    state,

    actions,

    mutations

});

로그인 후 복사
로그인 후 복사
  • Mutations

中文意思是“变化”,利用它可以来更改状态,本质上就是用来处理数据的函数。
store.commit(mutationName)是用来触发一个mutation的方法。
需要记住的是,定义的mutation必须是同步函数。

1

2

3

4

5

6

7

8

const mutations = {

    changState(state) {

        // 在这里改变state中的数据

    }

};

 

// 可以在组件中这样触发

this.$store.commit('changeState');

로그인 후 복사
로그인 후 복사
  • Actions

Actions也可以用于改变状态,不过是通过触发mutation实现的,重要的是可以包含异步操作。

直接触发可以使用this.$store.dispatch(actionName)方法。

简单的多组件数据交互

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

import Vue from 'vue';

import Vuex from 'vuex';

 

Vue.use(Vuex);

 

// 状态

const state = {

  singer: {}

};

 

// 跟踪状态的变化

const mutations = {

  setSinger (state, singer) {

    state.singer = singer;

  }

};

 

// 实例化store对象

export default new Vuex.Store({

  state,

  mutations

});

 

// 在singer组件中提交数据

this.$store.commit('setSinger',singer);

 

// 在singer-detail组件中接收数据

let singer = this.$store.state.singer;

로그인 후 복사
로그인 후 복사

vuex稍微复杂点的使用

在上面的小栗子中,我们把satemutations等其他一些内容写在了一起,
但是这种方式不适合大型点的项目。最好能将这些内容拎出来,单独作为一个文件来使用。

在src/store目录中新建以下文件:

  • state.js 用于存储状态信息

1

2

3

4

5

const sate = {

    singer: {}    

};

 

export default state;

로그인 후 복사
로그인 후 복사
  • mutation-types.js 保存一些常量(mutations中函数的函数名)

1

export const SET_SINGER = 'SET_SINGER';

로그인 후 복사
로그인 후 복사
  • mutations.js 用于更改状态(state中的数据)

1

2

3

4

5

6

7

8

9

10

import * as types from './mutation-types';

 

// 通过这个函数可以传入payload信息

const mutations = {

    [types.SET_SINGER](state,singer){

        state.singer = singer;

    }

};

 

export default mutations;

로그인 후 복사
로그인 후 복사
  • getters.js 对状态获取的封装

1

export const singer = state => state.singer;

로그인 후 복사
로그인 후 복사
  • actions.js 对mutation进行封装,或者执行一些异步操作

1

// 暂时没有什么异步操作

로그인 후 복사
로그인 후 복사
  • index.js store的入口文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

// 入口文件

import Vue from 'vue';

import Vuex from 'vuex';

import state from './state';

import mutations from './mutations';

import * as actions from './actions';

import * as getters from './getters';

import createLogger from 'vuex/dist/logger';

 

Vue.use(Vuex);

 

// 调试环境下开启严格模式

const debug = process.env.NODE_ENV !== 'production';

 

// 创建store对象并导出

export default new Vuex.Store({

  state,

  actions,

  getters,

  mutations,

  strict: debug,

  plugins: debug ? [createLogger()] : []

});

로그인 후 복사
로그인 후 복사

使用:

1

2

// main.js中引入

import store from './store';

로그인 후 복사
로그인 후 복사

有了以上内容,那么我们就可以在业务中去使用了:

例如:多组件之间的的数据交互。
需求:singer组件中需要将用户点击的那个singer对象传递给组件singer-detail组件。

singer.vue 组件中:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// 使用这个语法糖

import { mapMutations } from 'vuex';

 

methods:{

    ...mapMutations({

        // 将这个函数(setSinger)和mutations中用于修改状态的函数关联起来

        setSinger: 'SET_SINGER'

    });

}

 

// 传参

this.setSinger(singer);

 

// 语法糖的本质

  this.$store.commit('setSinger', singer);

로그인 후 복사
로그인 후 복사

singer-detail.vue 组件中:
我们就可以去使用这个数据了,当然也是使用我们的语法糖啦。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

import { mapGetters } from 'vuex';

 

export default {

    // 使用一个计算属性

    computed: {

        ...mapGetters([

            'singer'   // 这个就是getters.js中的那个singer

        ]);

    },

    created(){

        console.log(this.singer);

    }    

}

 

// 语法糖的本质:

let singer = this.$store.state.singer;

로그인 후 복사
로그인 후 복사

js中给CSS添加prefix

我们一定遇到过这种情况:
需要用JS写CSS动画。但我们又不得不处理前缀的问题。

所以一般是这样写的:

1

2

3

this.$refs.image.style.transform = `scale(${scale})`;

this.$refs.image.style.webkitTansform = `scale(${scale})`;

...

로그인 후 복사
로그인 후 복사

那么问题来了,怎样用JS处理这种情况呢?

思路:

  • 检测浏览器的能力。

  • 返回带着前缀的CSS样式。

代码实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

let elementStyle = document.createElement('p').style;

 

// 得到合适的浏览器前缀

let vendor = (() => {

  let transformNames = {

    webkit: 'webkitTransform',

    Moz: 'MozTransform',

    O: 'OTransform',

    ms: 'msTransform',

    standard: 'transform'

  };

 

  for (let key in transformNames) {

    let support = elementStyle[transformNames[key]] !== undefined;

    if (support) {

      return key;

    }

  }

  return false;

})();

 

// 对外暴露的方法

export function prefixStyle (style) {

  if (vendor === false) {

    return style;

  }

  if (vendor === 'standard') {

    return style;

  }

  let result = vendor + style.charAt(0).toUpperCase() + style.substr(1);

  return result;

}

로그인 후 복사
로그인 후 복사

使用案例:

1

2

3

4

5

6

7

8

// 导入该模块

import { prefixStyle } from 'common/js/dom';

 

// 加了合适前缀的CSS属性

const TRANSFORM = prefixStyle('transform');

 

// 使用该CSS属性

this.$refs.image.style[TRANSFORM] = `scale(${scale})`;

로그인 후 복사
로그인 후 복사

移动端的touch事件

随着触屏设备的普及,w3c为移动端web新增了touch事件。

最基本的touch事件包括4个事件:

  • touchstart 当在屏幕上按下手指时触发

当用户手指触摸到的触摸屏的时候触发。事件对象的 target 就是 touch 发生位置的那个元素。

  • touchmove 当在屏幕上移动手指时触发

即使手指移出了 原来的target元素,但 touchmove 仍然会被一直触发,而且 target 仍然是原来的 target 元素。

  • touchend 当在屏幕上抬起手指时触发

当用户的手指抬起的时候,会触发 touchend 事件。如果用户的手指从触屏设备的边缘移出了触屏设备,也会触发 touchend 事件。

touchend 事件的 target 也是与 touchstarttarget 一致,即使已经移出了元素。

  • touchcancel 当一些更高级别的事件发生的时候(如电话接入或者弹出信息)会取消当前的touch操作,即触发touchcancel。一般会在touchcancel时暂停游戏、存档等操作。

如果你使用了触摸事件,可以调用 event.preventDefault()来阻止鼠标事件被触发。

与移动端相关的interface主要有三个:

  • TouchEvent 表示触摸状态发生改变时触发的event

可以通过检查触摸事件的 TouchEvent.type 属性来确定当前事件属于哪种类型。

1

2

3

4

5

6

7

dom.addEventListener('touchstart',(e) => {

    // 获取事件类型

    let type = e.type;

    // toch事件发生时那个位置的元素对象

    let target = e.target;    

     

});

로그인 후 복사
  • Touch 表示用户和触屏设备之间接触时单独的交互点(a single point of contact)

screenXscreenY:触点相对于屏幕左边缘或上边缘的x、y坐标。
clientXclientY:触点相对于浏览器viewport左边缘或上边缘的x、y坐标。(不包含滚动距离)

pageXpageY:触点相对于document的左边缘或上边缘的x、y坐标。与client不同的是,包含左边滚动的距离。

target:触摸开始时的element。

1

2

3

4

5

6

7

8

9

10

// 获取touchList

let touchList = e.changedTouches;

// 获取第i个touch对象

let touch = touchList[i];

 

touch.screenX

touch.clientX

touch.pageX

touch.target

...

로그인 후 복사
  • TouchList 表示一组touches。当发生多点触摸的时候才用的到。

如果一个用户用三根手指接触屏幕(或者触控板), 与之相关的TouchList对于每根手指都会生成一个 Touch 对象, 共计 3 个.
可以通过三种方式获取这个对象:

1

2

3

4

5

6

7

8

dom.addEventListener('touchstart',(e) => {

    // 这个 TouchList对象列出了和这个触摸事件对应的那些发生了变化的 Touch 对象

    e.changedTouches

    // 这个TouchList列出了那些 touchstart发生在这个元素,并且还没有离开 touch surface 的touch point(手指)

    e.targetTouches

    // 这个 TouchList 列出了事件触发时: touch suface上所有的 touch point。

    e.touches

});

로그인 후 복사

播放器内核开发

audio标签

对于音乐的播放,我们使用了audio标签,监听它的事件和操作DOM,可以达到对音乐播放、
暂停、进度控制等操作。

1

2

<audio>

</audio>

로그인 후 복사

audio进行操作

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

let audio = this.$refs.audio;

// 暂停和播放

audio.pause();

audio.play();

 

// Audio对象的属性(部分)

 

audio.currentTime // 设置或返回音频中的当前播放位置(以秒计)。

 

audio.duration    // 返回音频的长度(以秒计)。

 

audio.loop    // 设置或返回音频是否应在结束时再次播放。(默认false)

 

audio.volume    // 设置或返回音频的音量。[0,1]

 

// Audio对象多媒体事件(Media Events)

 

onerror // 加载发生错误时的回调

 

ontimeupdate // 当播放位置改变时调用

updateTime(e) {

    if(this.currentSongReady){

        // 获取当前播放的进度

        this.currentSongTime=e.traget.currentTime;

    }

}

oncanplay // 能够播放时调用

 

// 通过监听这个事件,设置标志位,这个标志位可以帮助我们

// 防止用户快速切换歌曲引起一些错误。

songCanPlay(){

    this.currentSongReady = true;

}

 

 

onended // 到达结尾时调用

 

onplay、onpause...

로그인 후 복사

进度条组件

1.progress-bar.vue接收一个percent参数,用来显示当前播放的一个进度。

2.对于进度条用户手动拖动进度的实现。

1

2

<p>

</p>

로그인 후 복사

思路:主要是通过监听ontouchstartontouchmoveontouchend事件来完成。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

// 首先得定义一个`touch`对象

let touch = {};

 

// 在监听的方法中

touchStart(e){

    this.touch.initialized = true;

    // 获取touch的起始位置

    this.touch.startX = e.touches[0].pageX;

    // 获取整个进度条的宽度

    this.touch.barW = xxx;

    // 获取已经播放的进度

    this.touch.offset = xxx;    

}

 

touchMove(e){

    // 判断有无初始化

    ...

    // 获取用户滑动的距离

    let deltaX = e.touches[0].pageX - this.touch.startX;

    let barW = xxx; // 进度条的宽度 - 拖动btn的宽度

    let offset = Math.min(Math.max(0, this.touch.offset + detail), barW);

     

    // 最后设置btn的位置和progress的进度就OK

    ...

}

 

touchEnd(){

    this.touch.initialized = false;

    // 然后将进度推送出去就好了

    this.$emit('percentChange',percent);

}

로그인 후 복사

svg实现圆形进度条

1

2

3

4

5

6

7

8

9

<template>

  <p>

    <svg>

      <circle></circle>

      <circle></circle>

    </svg>

    <slot></slot>

  </p>

</template>

로그인 후 복사

通过svg可以实现各种进度条,有一个问题,怎样去动态的修改它的进度值呢?

这就不能不提 SVG Stroke 属性

  • stroke 定义一条线,文本或元素轮廓颜色

  • stroke-width 文本或元素轮廓的厚度

  • stroke-dasharray 该属性可用于创建虚线

  • stroke-dashoffset 设置虚线边框的偏移量

OK,知道了以上属性,就足以实现一个可设置进度的SVG进度条了。

思路:stroke-dasharray适用于创建虚线的,如果这个虚线长度为整个轮廓的周长呢。
stroke-dashoffset可以设置虚线的偏移量,利用这两个属性,我们就可以完成对进度的控制。

且看一个小栗子:
Vue 기반의 모바일 웹 뮤직 플레이어 구현

所以,通过父组件传入的percent,不断地修改stroke-dashoffset就能达到进度的显示了。

全屏和退出全屏

1

2

3

4

5

6

7

// 全屏显示

document.documentElement.webkitRequestFullScreen();

// 退出全屏

document.webkitExitFullscreen();

 

// 1.得根据不同的浏览器添加前缀

// 2.程序主动调用不管用,得用户操作才可以(点击按钮)

로그인 후 복사

歌词页的显示

通过网络接口获取的歌词:
Vue 기반의 모바일 웹 뮤직 플레이어 구현

对于歌词的解析,播放是通过一个插件lyric-parser完成的。

这个插件很简单:
1.通过正则把时间和对应的歌词切分出来创建成对象。
2.当调用play方法时,通过定时器完成歌词的播放,并将对应的行号和歌词通过回调函数传递出去。

当播放的歌词超过5行时,就可以使用封装的scroll组件完成滚动操作。

1

2

3

4

5

6

if (lineNum > 5) {

  let elements = this.$refs.lyricLine;

  this.$refs.lyricScroll.scrollToElement(elements[lineNum - 5], 1000);

else {

  this.$refs.lyricScroll.scrollTo(0, 0, 1000);

}

로그인 후 복사

Vue中的mixin

为什么要使用mixin?

多个组件公用一样的代码,我们可以将这部分抽离出来作为mixin,只要引入对应的组件中就可以了。

例如下面的mixin

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

import { mapGetters } from 'vuex';

 

export const playListMixin = {

 

  mounted () {

    this.handlePlayList(this.playList);

  },

  // 当路由对应的页面激活时调用

  activated () {

    this.handlePlayList(this.playList);

  },

  watch: {

    playList (newPlayList) {

      this.handlePlayList(newPlayList);

    }

  },

  computed: {

    ...mapGetters([

      'playList'

    ])

  },

  methods: {

      // 这个方法需要对应的组件自己去实现,直接调用抛出错误

    handlePlayList () {

      throw new Error('Components must implement handlePlayList method.');

    }

  }

};

로그인 후 복사

有了mixin我们在组件中就可以这样使用了:

1

2

3

4

5

6

import { playListMixin } from 'common/js/mixin';

 

export default{

    mixins: [playListMixin],

    ...

}

로그인 후 복사

节流处理

在搜索页面,我们需要处理用户的输入,然后向服务器发起请求。
为了不必要的请求、节省流量和提高页面性能,我们都有必要做节流处理。

在搜索框search-box这个基础组件中:

1

2

3

4

5

6

7

8

9

10

// 在created钩子中,我们监听用户输入字符串(query)变化,然后将变化后的字符串

// 提交给父组件

 

// 可以看到在回调函数中,又包了一层debounce函数

 

created () {

  this.$watch('query', debounce(() => {

    this.$emit('queryChange', this.query);

  }, 500));

}

로그인 후 복사

所以debounce函数,就是我们的节流函数,这个函数,接收一个函数,返回一个新的函数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

function debounce(func,delay){

    let timer = null;

    return function(...args){

        if(timer){

            clearTimeout(timer);

        }

        timer = setTimeout(()=>{

            func.apply(this,args);

        },delay)        

    }

}

 

// 测试

function show(){

    console.log('hello...');

}

 

var func = debounce(show,3000);

 

// 调用

func(); 

 

// 连续调用时,没有超过三秒是不会有任何输出的

로그인 후 복사

animation动画

语法:

animation: name duration timing-function delay iteration-count direction fill-mode play-state;
animation: 动画名称 执行时间 速度曲线 延时时间 执行次数 动画播放顺序 结束时应用的样式 播放的状态(paused|running)

封装localStorage操作

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

const __VERSION__ = '1.0.1';

const store = {

  version: __VERSION__,

  storage: window.localStorage,

  session: {

    storage: window.sessionStorage

  }

};

 

// 操作store的api

const api = {

  set (key, val) {

    if (this.disabled) {

      return false;

    }

    if (val === undefined) {

      return this.remove(key);

    }

    this.storage.setItem(key, this.serialize(val));

    return val;

  },

  get (key, val) {

    if (this.disabled) {

      return false;

    }

    let result = this.storage.getItem(key);

    if (!result) {

      return val;

    }

    return this.deSerialize(result);

  },

  getAll () {

    if (this.disabled) {

      return false;

    }

    let ret = {};

    for (let key in this.storage) {

      if (this.storage.hasOwnProperty(key)) {

        ret[key] = this.get(key);

      }

    }

    return ret;

  },

  remove (key) {

    if (this.disabled) {

      return false;

    }

    this.storage.removeItem(key);

  },

  removeAll () {

    if (this.disabled) {

      return false;

    }

    this.storage.clear();

  },

  forEach (cb) {

    if (this.disabled) {

      return false;

    }

    for (let key in this.storage) {

      if (this.storage.hasOwnProperty(key)) {

        cb && cb(key, this.get(key));

      }

    }

  },

  has (key) {

    if (this.disabled) {

      return false;

    }

    return key === this.get(key);

  },

  serialize (val) {

    try {

      return JSON.stringify(val) || undefined;

    catch (e) {

      return undefined;

    }

  },

  deSerialize (val) {

    if (typeof val !== 'string') {

      return undefined;

    }

    try {

      return JSON.parse(val) || undefined;

    catch (e) {

      return undefined;

    }

  }

};

 

// 扩展store对象

Object.assign(store, api);

Object.assign(store.session, api);

 

// 浏览器能力检测

try {

  let testKey = 'test_key';

  store.set(testKey, testKey);

  if (store.get(testKey) !== testKey) {

    store.disabled = true;

  }

  store.remove(testKey);

catch (e) {

  store.disabled = true;

}

 

export default store;

로그인 후 복사

路由懒加载

为什么需要?

如果开发的App太大的话,就会导致首屏渲染过慢,为了增强用户体验,加快渲染速度,
需要用到懒加载功能。让首屏的内容先加载出来,其他路由下的组件按需加载。

vue官网描述:

异步组件
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。
为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。
Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。

1

2

3

4

5

6

7

8

9

10

11

12

13

const AsyncComponent = () => ({

  // 需要加载的组件 (应该是一个 `Promise` 对象)

  component: import('./MyComponent.vue'),

  // 异步组件加载时使用的组件

  loading: LoadingComponent,

  // 加载失败时使用的组件

  error: ErrorComponent,

  // 展示加载时组件的延时时间。默认值是 200 (毫秒)

  delay: 200,

  // 如果提供了超时时间且组件加载也超时了,

  // 则使用加载失败时使用的组件。默认值是:`Infinity`

  timeout: 3000

})

로그인 후 복사

注意:如果你希望在 Vue Router 的路由组件中使用上述语法的话,你必须使用 Vue Router 2.4.0+ 版本。

当然为了简单起见:

router/index.js路由配置文件中这样加载组件:

1

2

3

4

// import Recommend from '@/components/recommend/recommend';

const Recommend = () => ({

  component: import('@/components/recommend/recommend')

});

로그인 후 복사

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

JavaScript中Array数组的tips的讲解

위 내용은 Vue 기반의 모바일 웹 뮤직 플레이어 구현의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

HTML의 테이블 테두리 HTML의 테이블 테두리 Sep 04, 2024 pm 04:49 PM

HTML의 테이블 테두리 안내. 여기에서는 HTML의 테이블 테두리 예제를 사용하여 테이블 테두리를 정의하는 여러 가지 방법을 논의합니다.

HTML의 중첩 테이블 HTML의 중첩 테이블 Sep 04, 2024 pm 04:49 PM

HTML의 Nested Table에 대한 안내입니다. 여기에서는 각 예와 함께 테이블 내에 테이블을 만드는 방법을 설명합니다.

HTML 여백-왼쪽 HTML 여백-왼쪽 Sep 04, 2024 pm 04:48 PM

HTML 여백-왼쪽 안내. 여기에서는 HTML margin-left에 대한 간략한 개요와 코드 구현과 함께 예제를 논의합니다.

HTML 테이블 레이아웃 HTML 테이블 레이아웃 Sep 04, 2024 pm 04:54 PM

HTML 테이블 레이아웃 안내. 여기에서는 HTML 테이블 레이아웃의 값에 대해 예제 및 출력 n 세부 사항과 함께 논의합니다.

PHP에서 HTML/XML을 어떻게 구문 분석하고 처리합니까? PHP에서 HTML/XML을 어떻게 구문 분석하고 처리합니까? Feb 07, 2025 am 11:57 AM

이 튜토리얼은 PHP를 사용하여 XML 문서를 효율적으로 처리하는 방법을 보여줍니다. XML (Extensible Markup Language)은 인간의 가독성과 기계 구문 분석을 위해 설계된 다목적 텍스트 기반 마크 업 언어입니다. 일반적으로 데이터 저장 AN에 사용됩니다

HTML에서 텍스트 이동 HTML에서 텍스트 이동 Sep 04, 2024 pm 04:45 PM

HTML에서 텍스트 이동 안내. 여기서는 Marquee 태그가 구문과 함께 작동하는 방식과 구현할 예제에 대해 소개합니다.

HTML 정렬 목록 HTML 정렬 목록 Sep 04, 2024 pm 04:43 PM

HTML 순서 목록에 대한 안내입니다. 여기서는 HTML Ordered 목록 및 유형에 대한 소개와 각각의 예에 대해서도 설명합니다.

HTML 온클릭 버튼 HTML 온클릭 버튼 Sep 04, 2024 pm 04:49 PM

HTML onclick 버튼에 대한 안내입니다. 여기에서는 각각의 소개, 작업, 예제 및 다양한 이벤트의 onclick 이벤트에 대해 설명합니다.

See all articles