需求:从左侧面板中拖拽控件到右侧画布区域,然后生成一个拖拽控件类型控件,需要修改拖拽时的样式
因为没有找到合适的方式去修改拖拽中的样式和拖拽到非画布区域返回动画,所以手写了个utils
ps: 改utils是ts版本的,如果需要js版本的,可以手动编译为js
* 使用原生鼠标移动事件模拟拖拽
type
Nullable
<
T
>
=
T
|
null
|
undefined
type
WatchEvent
=
'before'
|
'start'
|
'moving'
|
'end'
const
isWatchEvent
:
Function
=
(
type
:
unknown
)
:
type
is
string
=>
typeof
type
===
'string'
const
isWatchEventList
:
Function
=
(
type
:
unknown
)
:
type
is
Array
<
WatchEvent
>
=>
type
instanceof
Array
&&
type
.
some
(
(
s
:
unknown
)
=>
isWatchEvent
(
s
)
)
const
isFunction
:
Function
=
(
type
:
unknown
)
:
type
is
Function
=>
type
instanceof
Function
&&
typeof
type
===
'function'
interface
Component
{
name
:
string
,
icon
?
:
any
,
type
?
:
string
,
id
:
string
|
number
interface
DragOptions
{
dragList
:
Array
<
HTMLElement
>
|
HTMLElement
,
container
?
:
HTMLElement
,
componentList
?
:
Array
<
Component
|
any
>
,
dragClass
?
:
string
,
isDeleteDrag
?
:
Boolean
,
isInit
?
:
Boolean
interface
WatchItem
{
event
:
WatchEvent
,
callback
:
Array
<
Function
>
interface
Position
{
x
:
number
,
y
:
number
}
class
SimulationDrag
{
readonly watchEvent
:
Array
<
WatchEvent
>
=
[
'before'
,
'start'
,
'moving'
,
'end'
]
private
readonly _id
=
'data-dragId'
private
_dragList
:
Array
<
HTMLElement
>
private
_container
!
:
HTMLElement
private
_componentList
!
:
Array
<
Component
>
private
_dragClass
?
:
string
private
_dragIngDom
:
Nullable
<
HTMLElement
>
private
_parent
:
HTMLElement
private
_position
:
Position
=
{
x
:
0
,
y
:
0
}
private
_isDeleteDrag
:
Boolean
=
false
private
_watchList
:
Array
<
WatchItem
>
=
[
]
private
_isInit
:
Boolean
=
true
constructor
(
optins
:
DragOptions
)
{
this
.
_dragList
=
optins
.
dragList
instanceof
Array
?
optins
.
dragList
:
[
optins
.
dragList
]
const
parentElement
:
Nullable
<
HTMLElement
>
=
this
.
_dragList
[
0
]
.
parentElement
this
.
_parent
=
parentElement
?
?
document
.
body
this
.
_container
=
optins
.
container
?
?
document
.
body
this
.
_componentList
=
optins
.
componentList
?
?
[
]
this
.
_dragClass
=
optins
.
dragClass
this
.
_isDeleteDrag
=
<
Boolean
>
optins
.
isDeleteDrag
if
(
optins
.
isInit
!==
undefined
)
{
this
.
_isInit
=
optins
.
isInit
this
.
_addData_id
(
)
this
.
_isInit
&&
this
.
_initMouseEvent
(
)
public
init
(
)
{
this
.
_initMouseEvent
(
)
* clone dom 元素
* @param {HTMLElement} brother 需要克隆的元素
private
_cloneDom
(
brother
:
HTMLElement
)
:
HTMLElement
|
Node
{
const
{
left
,
top
,
width
,
height
}
=
brother
.
getBoundingClientRect
(
)
const
children
:
HTMLElement
=
<
HTMLElement
>
brother
.
cloneNode
(
true
)
const
classList
:
string
=
brother
.
classList
.
toString
(
)
children
.
classList
.
add
(
classList
)
this
.
_dragClass
&&
children
.
classList
.
add
(
this
.
_dragClass
)
children
.
ondragstart
=
(
)
=>
false
this
.
_setStyle
(
children
,
{
position
:
'fixed'
,
left
:
left
+
'px'
,
top
:
top
+
'px'
,
width
:
width
+
'px'
,
height
:
height
+
'px'
,
cursor
:
'all-scroll'
,
zIndex
:
'2021'
return
children
private
_addData_id
(
)
{
const
getId
:
any
=
(
index
:
number
)
=>
this
.
_componentList
?
.
[
index
]
?
.
id
?
?
new
Date
(
)
.
valueOf
(
)
.
toString
(
)
this
.
_dragList
.
forEach
(
(
item
:
HTMLElement
,
index
:
number
)
=>
item
.
setAttribute
(
this
.
_id
,
getId
(
index
)
as
string
)
)
private
_initMouseEvent
(
)
{
this
.
emitWatch
(
'before'
)
document
.
addEventListener
(
'mousedown'
,
(
e
:
MouseEvent
)
=>
this
.
dragEvent
(
e
)
)
document
.
addEventListener
(
'keydown'
,
(
)
=>
this
.
removeDrag
(
)
)
public
dragEvent
(
e
:
MouseEvent
)
{
if
(
!
this
.
_parent
.
contains
(
<
Node
>
e
.
target
)
)
return
this
.
emitWatch
(
'start'
,
e
)
document
.
getSelection
(
)
?
.
empty
(
)
const
target
:
HTMLElement
=
<
HTMLElement
>
e
.
target
const
dragDom
:
Nullable
<
HTMLElement
>
=
this
.
_dragList
.
find
(
(
drag
:
HTMLElement
)
=>
drag
===
target
||
drag
.
contains
(
target
)
)
if
(
dragDom
)
{
this
.
_dragIngDom
=
<
HTMLElement
>
this
.
_cloneDom
(
dragDom
)
this
.
_parent
?
.
appendChild
(
this
.
_dragIngDom
)
this
.
_position
.
x
=
e
.
clientX
this
.
_position
.
y
=
e
.
clientY
const
_dragMoving
=
this
.
_dragMoving
.
bind
(
this
)
document
.
addEventListener
(
'mousemove'
,
_dragMoving
)
document
.
onmouseup
=
(
ev
:
MouseEvent
)
=>
{
if
(
!
this
.
_parent
.
contains
(
<
Node
>
ev
.
target
)
)
return
document
.
removeEventListener
(
'mousemove'
,
_dragMoving
)
!
this
.
judgePosition
(
)
&&
this
.
removeDrag
(
)
this
.
emitWatch
(
'end'
,
ev
)
public
removeDrag
(
)
{
this
.
_dragIngDom
?
.
parentElement
?
.
removeChild
(
this
.
_dragIngDom
)
* 根据鼠标移动拖拽元素
* @param ev 鼠标移动的事件参数
private
_dragMoving
(
ev
:
MouseEvent
)
{
document
.
getSelection
(
)
?
.
empty
(
)
if
(
this
.
_dragIngDom
)
{
this
.
emitWatch
(
'moving'
,
ev
)
const
{
clientX
,
clientY
}
=
ev
const
left
=
`
${
parseFloat
(
this
.
_dragIngDom
.
style
.
left
)
+
clientX
-
this
.
_position
.
x
}
px`
const
top
=
`
${
parseFloat
(
this
.
_dragIngDom
.
style
.
top
)
+
clientY
-
this
.
_position
.
y
}
px`
this
.
_setStyle
(
this
.
_dragIngDom
,
{
left
,
top
}
)
this
.
_position
.
x
=
clientX
this
.
_position
.
y
=
clientY
* 判断当前移动的控件是否放入到了容器中
* @returns 返回结果
private
judgePosition
(
)
:
Boolean
{
if
(
this
.
_isDeleteDrag
)
return
!
this
.
_isDeleteDrag
if
(
this
.
_container
===
document
.
body
)
return
true
const
{
left
:
Pleft
,
top
:
Ptop
,
right
:
Pright
,
bottom
:
Pbottom
}
=
<
DOMRect
>
this
.
_container
?
.
getBoundingClientRect
(
)
const
{
left
:
Cleft
,
top
:
Ctop
,
right
:
Cright
,
bottom
:
Cbottom
}
=
<
DOMRect
>
this
.
_dragIngDom
?
.
getBoundingClientRect
(
)
return
(
Cleft
>=
Pleft
&&
Cright
<=
Pright
)
&&
(
Ctop
>=
Ptop
&&
Cbottom
<=
Pbottom
)
* 设置样式
* @param {HTMLElement} dom 需要设置样式的 dom
* @param {Object} styles 需要设置的样式
private
_setStyle
(
dom
:
HTMLElement
,
styles
:
Object
)
{
Object
.
assign
(
dom
.
style
,
styles
)
* 监听事件回调
* @param watchType 需要监听的类型,如果不传递默认为监听全部,可以监听单个或者以数组形式监听某几个
* @param callback 事件回调,如果没有传递监听,则callback为第一个参数
public
watch
(
watchType
:
Array
<
WatchEvent
>
|
WatchEvent
|
Function
,
callback
?
:
Function
)
:
void
|
never
{
if
(
isWatchEventList
(
watchType
)
&&
callback
&&
isFunction
(
callback
)
)
{
this
.
addWatch
(
(
watchType
as
Array
<
WatchEvent
>
)
,
callback
)
}
else
if
(
isWatchEvent
(
watchType
)
&&
callback
&&
isFunction
(
callback
)
)
{
this
.
addWatch
(
[
(
watchType
as
WatchEvent
)
]
,
callback
)
}
else
if
(
isFunction
(
watchType
)
)
{
this
.
addWatch
(
this
.
watchEvent
,
<
Function
>
watchType
)
}
else
{
throw
new
Error
(
`
${
watchType
}
is unknown`
)
* 给 watchList 增加回调
* @param watchType 需要处理的数组
* @param callback 回调
private
addWatch
(
watchType
:
Array
<
WatchEvent
>
,
callback
:
Function
)
{
watchType
.
forEach
(
(
event
:
WatchEvent
)
=>
{
const
watchItem
:
Nullable
<
WatchItem
>
=
this
.
_watchList
?
.
find
(
(
item
:
WatchItem
)
=>
event
===
item
.
event
)
if
(
watchItem
)
{
watchItem
.
callback
.
push
(
callback
)
}
else
{
this
.
_watchList
?
.
push
(
{
event
,
callback
:
[
callback
]
* 根据 event 触发相应事件
* @param event 需要触发的事件
private
emitWatch
(
event
:
WatchEvent
,
args
?
:
unknown
)
{
if
(
args
&&
typeof
args
===
'object'
)
{
args
=
Object
.
assign
(
args
,
{
eventType
:
event
,
position
:
{
...
this
.
_position
}
if
(
this
.
_watchList
.
length
)
{
const
callbacks
:
Nullable
<
Array
<
Function
>>
=
this
.
_watchList
.
find
(
(
item
:
WatchItem
)
=>
item
.
event
===
event
)
?
.
callback
callbacks
?
.
forEach
(
(
callback
:
Function
)
=>
callback
(
args
)
)
export
default
SimulationDrag
<template>
<div class="component-list-content">
<div class="search">
<el-input v-model="keywords" placeholder="请输入" clearable></el-input>
</div>
<div class="component-list">
<div class="title">控件列表</div>
<div class="components">
<div :ref="getDrag" class="component-item" v-for="component in getComponentList(keywords)" :key="component.id">
<img class="component-img" :src="component.icon">
<span class="component-name">{{component.name}}</span>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, Ref, onMounted, computed, ComputedRef } from 'vue'
import componentIcon from '@/assets/images/component.png'
type ComponentItem = {
name: string,
id: string | number,
type: string,
icon?: any,
activeIcon?: any
import SimulationDrag from '@utils/drag/index'
export default defineComponent({
name: "component-list",
setup () {
const keywords: Ref<string> = ref('')
const componentList: Ref<Array<ComponentItem>> = ref([])
const dragList: Array<HTMLElement> = []
const getDrag = (el: HTMLElement) => {
if (el) {
dragList.push(el as HTMLElement)
for (let i: number = 0; i < 50; i++) componentList.value.push({
name: `控件-${i + 1}`,
id: i,
type: 'input',
icon: componentIcon,
activeIcon: componentIcon
const getComponentList:ComputedRef = computed(() => (keywords: string): Array<ComponentItem> => componentList.value.filter((f: ComponentItem) => f.name.includes(keywords)))
onMounted(() => {
const drag: SimulationDrag = new SimulationDrag({
dragList,
componentList: componentList.value,
dragClass: 'custom-draggable-style',
container: document.getElementById('componentMain') as HTMLElement
drag.watch((e: any) => {
console.log(e.position)
return {
keywords,
componentList,
getComponentList,
getDrag
</script>
<style lang="scss" scoped>
.component-list-content {
@extend .wh100p;
background-color: #fff;
box-shadow: $design-shadow;
@extend .flex-column;
.search {
width: 100%;
padding: 10px;
.component-list {
padding-top: 10px;
overflow: auto;
flex: 1;
.title {
padding-bottom: 10px;
border-bottom: 1px solid #ccc;
.components {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
.component- {
&item {
@extend .flex-column;
width: 33%;
height: 70px;
text-align: center;
padding: 10px;
align-items: center;
&img {
width: 12px;
height: 12px;
margin-bottom: 15px;
.custom-draggable-style {
background-color: $default-color2;
</style>
需求:从左侧面板中拖拽控件到右侧画布区域,然后生成一个拖拽控件类型控件,需要修改拖拽时的样式因为没有找到合适的方式去修改拖拽中的样式和拖拽到非画布区域返回动画,所以手写了个utilsps: 改utils是ts版本的,如果需要js版本的,可以手动编译为js/** * 使用原生鼠标移动事件模拟拖拽 */type Nullable<T> = T | null | undefined// 监听的事件类型type WatchEvent = 'before' | 'start' | 'm
如何实现canvas根据父容器进行自适应?
Ant Design的组件都提供了强大的自适应能力,为了对齐父组件,镶嵌在Ant Design组件里的canvas也需要能根据父级容器进行自适应的能力,页面高度一般不用做自适应,因为即使太多内容页面太长,也会出现滚动条,所以问题不大,问题在于宽度,屏幕尺寸各不同,如果宽度不够,元素就会被挤变形或者换行
首先,固定canvas的height,...
原文链接:http://mp.weixin.qq.com/s?__biz=MzAwNjI5MTYyMw==&mid=2651493053&idx=1&sn=8409bc267cd73229425a915f27f6a27f&scene=0#wechat_redirect
1.安卓浏览器看背景图片,有些设备会模糊。
用同等比例的图片在PC机上很清楚,但是手机上很...
age has been scrolled
var pageTop = this.pieDish.offset().top;
var pageLeft = this.pieDish.offset().left;
this.pieCenter = {x: this.startPieCenter.x - (this.pageLeft - pageLeft) , y:this.startPieCenter.y - (this.pageTop - pageTop)};
// differentiate