精读 Vue 官方文档系列 🎉
内嵌组件
使用 Vue 兼容第三方生态的范例。例如基于 JavaScript 或 Jquery 的插件、widget、库等。
一些诀窍:
- 选用的插件、widget 最好自带样式作用域隔离,比如基于
BEM。 - 需要依赖 DOM 元素的,应该将插件的执行时期延缓到
mounted生命周期内,此时可以通过this.$el获取组件的根元素。 - 可以将插件的事件系统结合到 Vue 的程序化事件监听器中共同使用。
- 组件销毁时
destored要主动去消耗插件的实例,防止内存泄漏。
代码示例:
mounted: function () {
var vm = this;
$(this.$el)
// init select2
.select2({ data: this.options })
.val(this.value)
.trigger("change")
// emit event on change.
.on("change", function () {
//触发 v-model,将插件的事件转换为 Vue 的事件。
vm.$emit("input", this.value);
});
},
watch: {
value: function (value) {
// update value
$(this.$el).val(value).trigger("change");
},
options: function (options) {
// update options
$(this.$el).empty().select2({ data: options });
}
},
destroyed: function () {
//销毁组件
$(this.$el).off().select2("destroy");
}
具有伸缩性的 Header Example
<template>
<div
class="card"
@mousedown="startDrag"
@mousemove="onDrag"
@mouseup="stopDrag"
@mouseleave="stopDrag"
>
<div class="card__header" :style="computedStyle"> </div>
<div class="card__body"> </div>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
dragging: false, //是否按下,准备拖动
start: { x: 0, y: 0 }, //记录起点坐标
distance: { x: 0, y: 0 }, //记录拖动的坐标
};
},
computed: {
computedStyle() {
const dy = this.distance.y;
const dampen = dy > 0 ? 7 : 12;
const height = 160 + dy / dampen; //160px 是固定的头部高度,所以要加上去。
const radius = dy / 480 * 100;
return { height: height + "px", borderBottomLeftRadius: radius + "%",borderBottomRightRadius: radius + "%" };
},
},
methods: {
startDrag(e) {
e = e.changeTouches ? e.changeTouches : e;
this.dragging = true;
this.start.x = e.pageX;
this.start.y = e.pageY;
},
onDrag(e) {
e = e.changeTouches ? e.changeTouches : e;
if (this.dragging) {
this.distance.y = e.pageY - this.start.y;
}
},
stopDrag() {
if (this.dragging) {
this.dragging = false;
window.dynamics.animate(
this.distance,
{
x: 0,
y: 0,
},
{
type: window.dynamics.spring,
duration: 700,
friction: 280,
}
);
}
},
},
};
</script>
<style scoped>
.card {
width: 320px;
height: 480px;
background: #eee;
margin: 500px auto;
user-select: none;
}
.card__header {
height: 160px;
background: grey;
box-sizing: border-box;
padding: 30px;
transition: all .1s;
}
.card__body {
box-sizing: border-box;
padding: 30px;
}
</style>
- 我们需要通过
start来记录用户每次拖动时的起始坐标,通过拖动坐标减去起始坐标,得到的才是有效的拖动距离。 - 通过添加
draging标志结合mousemove事件,用来判断用户是否已经准备拖动以及是否已处于拖动中。 - 手动调参给出一个大致效果的阻尼值
dampen,并且通过判断的distance.y值的正负来应用不同的阻尼系数。 mouseleave事件不支持冒泡,所以进入其子元素内部不会触发离开事件。- 使用 dynamics 动画库实现物理动画。
最后
其实我对官方示例中的“阻尼”计算方式很困惑,官方中会在拖动过程中用实际的拖动距离除以一个阻尼值。
onDrag: function(e) {
e = e.changedTouches ? e.changedTouches[0] : e;
if (this.dragging) {
// dampen vertical drag by a factor
var dy = e.pageY - this.start.y;
var dampen = dy > 0 ? 1.5 : 4;
this.c.y = 160 + dy / dampen;
}
}
但是在计算属性中修改样式时又计算了一次阻尼。
{
computed:{
contentPosition: function() {
var dy = this.c.y - 160;
var dampen = dy > 0 ? 2 : 4;
return {
transform: "translate3d(0," + dy / dampen + "px,0)"
};
}
}
}
看上去应该只需要计算一次即可,没必要把拖动距离的计算分在两个地方,分别除以两个不同的阻尼系数,如果你能理解官方示例的真正含义,请麻烦在下面给我评论,感激不尽。
官方示例地址:cn.vuejs.org/v2/examples…