在开始之前我去复习了一下,clientX、clientY、pageX、pageY的区别,对于不熟悉offsetLeft和offsetTop的也可以在这里去复习一下。
vue拖拽指令之offsetX、clientX、pageX、screenX_wade3po的博客-CSDN博客_vue offset
客户区坐标位置(clientX,clientY)
鼠标事件都是在浏览器视口中的特定位置发生的。这个位置信息保存在事件对象的clientX和clientY属性中,所有浏览器都支持这两个属性。
页面坐标位置(pageX,pageY)
pageX和pageY两个属性代表鼠标光标在页面中的位置,因此坐标是从页面本身而非视口的左边和顶边计算的。在没有滚动的情况下,clientX和cilentY与pageX和pageY是相等的
屏幕坐标位置(screenX,screenY)
鼠标事件发生的时候,不仅仅会有相对于浏览器的窗口,还有一个相对于整个电脑屏幕的位置。
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
.box {
position: absolute;
top: 0;
left: 0;
text-align: center;
line-height: 100px;
width: 100px;
height: 100px;
border-radius: 10%;
background-color: greenyellow;
margin-top: 10px;
}
</style>
</head>
<body>
<div id="root">
<com-son :msg="haha"></com-son>
</div>
<template id="box">
<div>
<h3 class="box" v-for="(item,idx) in dmsg" :key="idx" @mousedown="drag">{{item}}</h3>
</div>
</template>
<script>
// 创建组件
let ComSon = {
template: "#box",
props: ['msg'],
data() {
return {
dmsg: this.msg
}
},
methods: {
drag(e) {
//鼠标摁下事件
var x = e.clientX - e.target.offsetLeft;
var y = e.clientY - e.target.offsetTop;
// console.log(x, y);
//鼠标移动事件
document.onmousemove = function (el) {
e.target.style.left = el.pageX - x + 'px'
e.target.style.top = el.pageY - y + 'px'
},
//鼠标抬起事件
document.onmouseup = function () {
// 设置随机颜色
var bgc = 'rgb(' + Math.floor(Math.random() * 256) + ',' + Math.floor(Math.random() * 256) + ',' + Math.floor(Math.random() * 256)
e.target.style.backgroundColor = bgc;
document.onmousemove = null;
}
}
}
}
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示
const vm = new Vue({
el: '#root',
data() {
return {
haha: ['宝子们', '周末愉快', '愉快吖', '哈哈', '~!!!', '这是', '作业哟']
}
},
methods: {
},
components: {
ComSon,
}
});
</script>
</body>
</html>
效果图:
案列二:自定义事件的子传父
1、css板块代码
#root {
width: 900px;
height: 600px;
padding: 10px;
display: flex;
justify-content: space-between;
}
.sh {
width: 400px;
height: 100%;
background-color: rgb(233, 157, 157);
padding: 10px;
}
.shop {
width: 100%;
height: 150px;
background-color: white;
display: flex;
align-items: center;
box-sizing: border-box;
overflow: hidden;
margin-bottom: 10px;
}
.bgcont {
width: 400px;
height: 100%;
background-color: rgb(157, 231, 233);
padding: 10px;
}
.shop img {
width: 120px;
margin-left: 10px;
}
.shop_context {
margin: 0px 10px;
}
.shop_context>p {
overflow: hidden;
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
font-size: 14px;
color: rgb(112, 111, 111);
}
.shop_context div {
display: flex;
justify-content: space-between;
align-items: center;
}
.shop_context div>p {
color: rgb(71, 70, 70);
font-size: 14px;
}
.active {
color: darkorange !important;
}
.son {
width: 100%;
height: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
align-items: center;
}
.son img {
margin-top: 20px;
width: 270px;
height: 290px;
border: darkgrey 1px solid;
}
.son p {
margin: 0px 20px;
font-size: 15px;
}
.son h2:last-of-type {
color: red;
}
.son button {
border: none;
width: 250px;
height: 45px;
border-radius: 10px;
background-image: linear-gradient(to right top, #d16ba5, #c777b9, #ba83ca, #aa8fd8, #9a9ae1, #8aa7ec, #79b3f4, #69bff8, #52cffe, #41dfff, #46eefa, #5ffbf1);
}
2、body部分
<body>
<div id="root">
<div class="sh">
<div class="shop" v-for="(item, index) in list" :key="index" @click="shopChuan(list[index])">
<img :src="item.imgsrc" alt="">
<div class="shop_context">
<h3>{{item.name}}</h3>
<p>{{item.context}}</p>
<div>
<h3>¥{{item.price}}缘</h3>
<p v-text="item.flag == true?'已购买':'未购买'" :class="item.flag == true?'active':''"></p>
</div>
</div>
</div>
</div>
<div class="bgcont" v-if="isShow">
<com-son :propsitem="propsitem" @haha="hahaha"></com-son>
</div>
</div>
3、vue代码
template代码是放在body中的,也可以将它写在组件里,依据个人喜好设置
<template id="son">
<div class="son">
<!-- <h1>子组件</h1> -->
<img :src="propsitem.imgsrc" alt="">
<h3>{{propsitem.name}}</h3>
<p>{{propsitem.context}}</p>
<h3>¥{{propsitem.price}}缘</h3>
<button @click="change">点击购买</button>
</div>
</template>
<script>
let ComSon = {
template: "#son",
props: {
// 对象验证
propsitem: {
type: Object
}
},
methods: {
change() {
if (this.propsitem.flag === true) {
alert("已经购买过了~~~")
return
}
this.$emit("haha", this.propsitem.id)
alert("购买成功~~~")
}
}
}
const vm = new Vue({
el: '#root',
data() {
return {
list: [
{
id: 111,
name: "小米平板5 Pro",
imgsrc: "./img/tp3.png",
context: "更大的屏幕,带来更强的拓展性高效的多任务处理灵活的多设备互联,你想做的每件事,从未如此得心应手小米平板5 Pro 12.4英寸生产力的更多可能性,由此展开。",
price: 2999,
flag: false,
},
{
id: 222,
name: "米家直驱洗烘一体机",
imgsrc: "./img/tp2.png",
context: "「以旧换新优惠50元,以旧换新到手价1949元!」「第六代DD直驱电机!智能洗烘!微蒸空气洗!高温杀菌,电机10年质保!」DD直驱电机/智能洗烘/微蒸空气洗/高温除螨洗/巴氏杀菌",
price: 1999,
flag: true,
},
{
id: 333,
name: "米家全能扫拖机器人",
imgsrc: "./img/tp1.png",
context: "免洗集尘全自动,一机解放双手,自动洗拖布 | 自动集尘 | 自动热烘干 | 自动补水 | 专用清洁剂,S-Cross AI™ 超感知立体识别避障系统,智能分配用水比例,拖地不中断",
price: 3999,
flag: false,
},
],
isShow: false,
propsitem: {}
}
},
methods: {
shopChuan(e) {
this.isShow = true
console.log(e);
this.propsitem = e
},
hahaha(b) {
// console.log(b);
for (let i of this.list) {
if (i.id === b) {
i.flag = true
// console.log(i.id);
}
}
}
},
components: {
ComSon
}
})
</script>
效果展示: