vangleer / es-drager

A draggable, resizable, rotatable component based on vue3
https://vangleer.github.io/es-drager/
336 stars 50 forks source link

left和top能否支持百分比的形式? #28

Closed qianjiachun closed 9 months ago

vangleer commented 10 months ago

这个可以考虑后期加上的

qianjiachun commented 10 months ago

好的好的,不仅是top和left,width和height希望也可以支持百分比的形式😂

vangleer commented 10 months ago

好的

vangleer commented 9 months ago

百分比解决方案

由于现有功能已足够实现百分比,就没有在内部支持,我们可以使用二次封装的方式实现

基于 es-drager 二次封装组件 MyDrager

<template>
  <Drager
    v-bind="dragData"
    boundary
    rotatable
    @change="onChange"
  >
    测试
  </Drager>
</template>

<script setup lang='ts'>
import Drager, { DragData } from 'es-drager/index'
import { computed, PropType } from 'vue'

const props = defineProps({
  parentRect: Object as PropType<DOMRect | null>, // 为了不频繁的去获取父级元素,采用传入的方式,这样就只获取一次即可
  width: {
    type: Number,
    default: 0
  },
  height: {
    type: Number,
    default: 0
  },
  left: {
    type: Number,
    default: 0
  },
  top: {
    type: Number,
    default: 0
  },
})
const dragData = computed(() => convert2percent(props as DragData, false))
const emit = defineEmits(['change'])

const onChange = (data: DragData) => {
  emit('change', convert2percent(data))
}

/**
 * 
 * @param data 转换的数据
 * @param flag true将值转成百分比,false将百分比值转换为像素
 */
const convert2percent = (data: DragData, flag = true) => {
  const result = { ...data }
  if (!props.parentRect) {
    return data
  }
  const keys = Object.keys(data)
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i] as keyof DragData
    if (['width', 'left'].includes(key)) {
      result[key] = Math.round(
        flag ? 
        result[key] / props.parentRect!.width * 100 : 
        result[key] / 100 * props.parentRect!.width
      )
    } else if (['height', 'top'].includes(key)) {
      result[key] = Math.round(
        flag ? 
        result[key] / props.parentRect!.height * 100 : 
        result[key] / 100 * props.parentRect!.height
      )
    }
  }

  return result
}
</script>

百分比主要设计,width、height、left、top属性,而且只会有传入百分比和在事件中得到最新的百分比数据两种情况。

内部需要进行转换,因此需要父级元素的宽高。考虑到性能原因,没有在MyDrager中获取,需手动传入

使用 MyDrager

<template>
  <div class="container" ref="containerRef">
    <button @click="handleClick">change</button>
    <MyDrager
      :parent-rect="parentRect"
      v-bind="dragData"
      rotatable
      @change="onChange"
    >
      测试
    </MyDrager>
  </div>
</template>

<script setup lang="ts">
import { DragData } from 'es-drager/index'
import MyDrager from '@/components/MyDrager.vue'
import { onMounted } from 'vue'
import { ref } from 'vue'
// 初始值,百分比
const dragData = ref({
  width: 50,
  height: 50,
  left: 0,
  top: 20
})
const containerRef = ref<HTMLElement | null>(null)
const parentRect = ref<DOMRect | null>(null)
const onChange = (data: DragData) => {
  Object.keys(data).forEach(key => {
    (dragData.value as any)[key] = (data as any)[key]
  })
  console.log(data, 'data')
}
// 手动修改
const handleClick = () => {
  dragData.value.width = 20
  dragData.value.height = 20
}
onMounted(() => {
  parentRect.value = containerRef.value?.getBoundingClientRect() || null
})
</script>

<style>
.container {
  position: relative;
  width: 500px;
  height: 500px;
  background-color: #ccc;
}
</style>

这里只处理了change事件,如有需要其它事件采取相同的方式即可