11111

<template>
<div class="ios-shake-advanced">
<div class="header">
<h2>iOS图标抖动 + 拖拽排序演示</h2>
<div class="controls">
<el-button
type="primary"
@click="startShaking"
:disabled="isShaking"
>
<i class="el-icon-edit"></i>
编辑模式
</el-button>
<el-button
type="success"
@click="stopShaking"
:disabled="!isShaking"
>
<i class="el-icon-check"></i>
完成
</el-button>
<el-button
type="info"
@click="resetIcons"
>
<i class="el-icon-refresh"></i>
重置
</el-button>
<el-button
type="warning"
@click="toggleDragMode"
:disabled="!isShaking"
>
<i class="el-icon-rank"></i>
{{ isDragMode ? '停止拖拽' : '拖拽排序' }}
</el-button>
</div>
<div class="status-info" v-if="isShaking">
<span class="status-badge">
{{ isDragMode ? '拖拽排序模式' : '删除模式' }}
</span>
</div>
</div>

<div class="icon-grid" ref="iconGridRef">
<div
v-for="(icon, index) in icons"
:key="icon.id"
class="icon-item"
:class="{
'shaking': isShaking,
'dragging': draggedItem === icon.id,
'drag-over': dragOverItem === icon.id && isDragMode
}"
:style="{
animationDelay: icon.delay,
transform: draggedItem === icon.id ? 'scale(1.1)' : ''
}"
:data-index="index"
:draggable="isShaking && isDragMode"
@click="handleIconClick(icon)"
@dragstart="handleDragStart($event, icon, index)"
@dragend="handleDragEnd"
@dragover="handleDragOver($event, index)"
@dragleave="handleDragLeave"
@drop="handleDrop($event, index)"
>
<div class="icon-container">
<div class="icon-bg" :style="{ backgroundColor: icon.color }">
<i :class="icon.iconClass"></i>
</div>
<div class="icon-label">{{ icon.name }}</div>
 
<!-- 删除按钮,只在非拖拽模式的抖动时显示 -->
<div
v-if="isShaking && !isDragMode"
class="delete-btn"
@click.stop="deleteIcon(icon.id)"
>
×
</div>
 
<!-- 拖拽指示器 -->
<div
v-if="isShaking && isDragMode"
class="drag-indicator"
>
<i class="el-icon-rank"></i>
</div>
</div>
</div>
</div>

<!-- 添加新图标的按钮 -->
<div class="add-icon-section" v-if="!isShaking">
<el-button
type="success"
icon="Plus"
@click="addRandomIcon"
circle
size="large"
/>
<span class="add-text">添加图标</span>
</div>

<!-- 操作提示 -->
<div class="help-panel">
<div class="help-title">操作说明:</div>
<ul class="help-list">
<li v-if="!isShaking">• 点击"编辑模式"开始编辑</li>
<li v-if="isShaking && !isDragMode">• 点击红色 × 删除图标</li>
<li v-if="isShaking && isDragMode">• 拖拽图标进行排序</li>
<li v-if="isShaking">• 点击"完成"结束编辑</li>
<li>• 点击图标查看详情(非编辑模式)</li>
</ul>
</div>
</div>
</template>

<script setup>
import { ref, onMounted, nextTick } from 'vue'
import { ElMessage } from 'element-plus'

const isShaking = ref(false)
const isDragMode = ref(false)
const draggedItem = ref(null)
const dragOverItem = ref(null)
const iconGridRef = ref(null)

// 图标数据
const icons = ref([
{ id: 1, name: '相机', iconClass: 'el-icon-camera', color: '#34D399', delay: '0s' },
{ id: 2, name: '消息', iconClass: 'el-icon-chat-line-round', color: '#3B82F6', delay: '0.1s' },
{ id: 3, name: '设置', iconClass: 'el-icon-setting', color: '#6B7280', delay: '0.2s' },
{ id: 4, name: '音乐', iconClass: 'el-icon-headset', color: '#EF4444', delay: '0.3s' },
{ id: 5, name: '地图', iconClass: 'el-icon-location', color: '#10B981', delay: '0.4s' },
{ id: 6, name: '日历', iconClass: 'el-icon-calendar', color: '#F59E0B', delay: '0.5s' },
{ id: 7, name: '邮件', iconClass: 'el-icon-message', color: '#8B5CF6', delay: '0.6s' },
{ id: 8, name: '天气', iconClass: 'el-icon-sunny', color: '#06B6D4', delay: '0.7s' },
{ id: 9, name: '购物', iconClass: 'el-icon-shopping-cart-2', color: '#EC4899', delay: '0.8s' },
{ id: 10, name: '游戏', iconClass: 'el-icon-trophy', color: '#F97316', delay: '0.9s' },
{ id: 11, name: '文件', iconClass: 'el-icon-folder', color: '#84CC16', delay: '1.0s' },
{ id: 12, name: '时钟', iconClass: 'el-icon-alarm-clock', color: '#A855F7', delay: '1.1s' }
])

// 可用的图标和颜色
const availableIcons = [
{ iconClass: 'el-icon-star-filled', name: '收藏' },
{ iconClass: 'el-icon-phone', name: '电话' },
{ iconClass: 'el-icon-video-camera', name: '视频' },
{ iconClass: 'el-icon-picture', name: '图片' },
{ iconClass: 'el-icon-notebook-1', name: '笔记' },
{ iconClass: 'el-icon-compass', name: '指南针' },
{ iconClass: 'el-icon-coffee-cup', name: '咖啡' },
{ iconClass: 'el-icon-bicycle', name: '单车' },
{ iconClass: 'el-icon-ship', name: '轮船' },
{ iconClass: 'el-icon-airplane', name: '飞机' }
]

const availableColors = [
'#EF4444', '#F97316', '#F59E0B', '#84CC16',
'#10B981', '#06B6D4', '#3B82F6', '#8B5CF6',
'#EC4899', '#F43F5E', '#6B7280', '#374151'
]

// 开始抖动
function startShaking() {
isShaking.value = true
isDragMode.value = false
ElMessage.success('进入编辑模式 - 可以删除图标!')
}

// 停止抖动
function stopShaking() {
isShaking.value = false
isDragMode.value = false
draggedItem.value = null
dragOverItem.value = null
ElMessage.info('退出编辑模式')
}

// 切换拖拽模式
function toggleDragMode() {
isDragMode.value = !isDragMode.value
if (isDragMode.value) {
ElMessage.success('开启拖拽排序模式')
} else {
ElMessage.info('关闭拖拽排序模式')
}
}

// 重置图标
function resetIcons() {
icons.value = [
{ id: 1, name: '相机', iconClass: 'el-icon-camera', color: '#34D399', delay: '0s' },
{ id: 2, name: '消息', iconClass: 'el-icon-chat-line-round', color: '#3B82F6', delay: '0.1s' },
{ id: 3, name: '设置', iconClass: 'el-icon-setting', color: '#6B7280', delay: '0.2s' },
{ id: 4, name: '音乐', iconClass: 'el-icon-headset', color: '#EF4444', delay: '0.3s' },
{ id: 5, name: '地图', iconClass: 'el-icon-location', color: '#10B981', delay: '0.4s' },
{ id: 6, name: '日历', iconClass: 'el-icon-calendar', color: '#F59E0B', delay: '0.5s' },
{ id: 7, name: '邮件', iconClass: 'el-icon-message', color: '#8B5CF6', delay: '0.6s' },
{ id: 8, name: '天气', iconClass: 'el-icon-sunny', color: '#06B6D4', delay: '0.7s' },
{ id: 9, name: '购物', iconClass: 'el-icon-shopping-cart-2', color: '#EC4899', delay: '0.8s' },
{ id: 10, name: '游戏', iconClass: 'el-icon-trophy', color: '#F97316', delay: '0.9s' },
{ id: 11, name: '文件', iconClass: 'el-icon-folder', color: '#84CC16', delay: '1.0s' },
{ id: 12, name: '时钟', iconClass: 'el-icon-alarm-clock', color: '#A855F7', delay: '1.1s' }
]
stopShaking()
ElMessage.success('图标已重置')
}

// 处理图标点击
function handleIconClick(icon) {
if (!isShaking.value) {
ElMessage.info(`打开 ${icon.name} 应用`)
}
}

// 删除图标
function deleteIcon(iconId) {
const index = icons.value.findIndex(icon => icon.id === iconId)
if (index > -1) {
const deletedIcon = icons.value[index]
icons.value.splice(index, 1)
ElMessage.warning(`删除了 ${deletedIcon.name}`)
}
}

// 添加随机图标
function addRandomIcon() {
const randomIcon = availableIcons[Math.floor(Math.random() * availableIcons.length)]
const randomColor = availableColors[Math.floor(Math.random() * availableColors.length)]
const newId = Math.max(...icons.value.map(icon => icon.id)) + 1
 
const newIcon = {
id: newId,
name: randomIcon.name,
iconClass: randomIcon.iconClass,
color: randomColor,
delay: `${(icons.value.length * 0.1) % 1.2}s`
}
 
icons.value.push(newIcon)
ElMessage.success(`添加了 ${newIcon.name}`)
}

// 拖拽开始
function handleDragStart(event, icon, index) {
if (!isDragMode.value) return
 
draggedItem.value = icon.id
event.dataTransfer.effectAllowed = 'move'
event.dataTransfer.setData('text/html', event.target.outerHTML)
event.dataTransfer.setData('text/plain', index.toString())
 
// 添加视觉反馈
setTimeout(() => {
event.target.style.opacity = '0.5'
}, 0)
}

// 拖拽结束
function handleDragEnd(event) {
event.target.style.opacity = '1'
draggedItem.value = null
dragOverItem.value = null
}

// 拖拽悬停
function handleDragOver(event, index) {
if (!isDragMode.value || !draggedItem.value) return
 
event.preventDefault()
event.dataTransfer.dropEffect = 'move'
 
const draggedIndex = icons.value.findIndex(icon => icon.id === draggedItem.value)
if (draggedIndex !== index) {
dragOverItem.value = icons.value[index].id
}
}

// 拖拽离开
function handleDragLeave() {
dragOverItem.value = null
}

// 拖拽放下
function handleDrop(event, dropIndex) {
if (!isDragMode.value || !draggedItem.value) return
 
event.preventDefault()
 
const draggedIndex = icons.value.findIndex(icon => icon.id === draggedItem.value)
 
if (draggedIndex !== dropIndex && draggedIndex !== -1) {
// 移动图标
const draggedIcon = icons.value[draggedIndex]
icons.value.splice(draggedIndex, 1)
icons.value.splice(dropIndex, 0, draggedIcon)
 
ElMessage.success(`${draggedIcon.name} 已移动`)
}
 
dragOverItem.value = null
}

onMounted(() => {
ElMessage.info('iOS高级图标抖动效果演示已加载')
})
</script>

<style scoped>
.ios-shake-advanced {
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: calc(100vh - 120px);
}

.header {
text-align: center;
margin-bottom: 30px;
color: white;
}

.header h2 {
margin-bottom: 20px;
font-size: 24px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}

.controls {
display: flex;
justify-content: center;
gap: 15px;
flex-wrap: wrap;
margin-bottom: 15px;
}

.status-info {
margin-top: 15px;
}

.status-badge {
display: inline-block;
background: rgba(255, 255, 255, 0.2);
color: white;
padding: 6px 16px;
border-radius: 20px;
font-size: 14px;
font-weight: 500;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
}

.icon-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 25px;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}

.icon-item {
position: relative;
cursor: pointer;
user-select: none;
transition: all 0.3s ease;
}

.icon-item.dragging {
opacity: 0.5;
transform: scale(1.1) rotate(5deg);
z-index: 1000;
}

.icon-item.drag-over {
transform: scale(1.05);
}

.icon-item.drag-over::before {
content: '';
position: absolute;
top: -5px;
left: -5px;
right: -5px;
bottom: -5px;
border: 2px dashed rgba(255, 255, 255, 0.8);
border-radius: 20px;
z-index: -1;
}

.icon-container {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}

.icon-bg {
width: 70px;
height: 70px;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transition: transform 0.2s ease;
position: relative;
overflow: hidden;
}

.icon-bg::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 50%);
border-radius: 16px;
}

.icon-bg i {
font-size: 28px;
color: white;
z-index: 1;
}

.icon-label {
margin-top: 8px;
font-size: 12px;
color: white;
text-align: center;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
font-weight: 500;
}

.icon-item:hover .icon-bg {
transform: scale(1.05);
}

.delete-btn {
position: absolute;
top: -8px;
right: 8px;
width: 24px;
height: 24px;
background: #ff3b30;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 16px;
font-weight: bold;
cursor: pointer;
box-shadow: 0 2px 8px rgba(255, 59, 48, 0.4);
transition: all 0.2s ease;
z-index: 10;
}

.delete-btn:hover {
transform: scale(1.1);
background: #ff1744;
}

.drag-indicator {
position: absolute;
top: -8px;
right: 8px;
width: 24px;
height: 24px;
background: rgba(255, 255, 255, 0.9);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #666;
font-size: 12px;
cursor: grab;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
transition: all 0.2s ease;
z-index: 10;
}

.drag-indicator:hover {
transform: scale(1.1);
background: white;
}

/* iOS风格的抖动动画 */
.shaking {
animation: shake 0.5s ease-in-out infinite alternate;
}

@keyframes shake {
0% {
transform: rotate(-1deg) translateY(-1px);
}
25% {
transform: rotate(1deg) translateY(1px);
}
50% {
transform: rotate(-1deg) translateY(-1px);
}
75% {
transform: rotate(1deg) translateY(1px);
}
100% {
transform: rotate(-1deg) translateY(-1px);
}
}

/* 更真实的iOS抖动效果 */
.shaking .icon-bg {
animation: iconShake 0.6s ease-in-out infinite;
}

@keyframes iconShake {
0% {
transform: rotate(-2deg) scale(1);
}
20% {
transform: rotate(2deg) scale(1.02);
}
40% {
transform: rotate(-1deg) scale(1.01);
}
60% {
transform: rotate(1deg) scale(1.02);
}
80% {
transform: rotate(-2deg) scale(1);
}
100% {
transform: rotate(2deg) scale(1.01);
}
}

/* 删除按钮和拖拽指示器的抖动 */
.shaking .delete-btn,
.shaking .drag-indicator {
animation: deleteShake 0.4s ease-in-out infinite alternate;
}

@keyframes deleteShake {
0% {
transform: scale(1) rotate(-5deg);
}
100% {
transform: scale(1.05) rotate(5deg);
}
}

.add-icon-section {
text-align: center;
margin-top: 40px;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}

.add-text {
color: white;
font-size: 14px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}

.help-panel {
max-width: 600px;
margin: 40px auto 0;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 16px;
padding: 20px;
border: 1px solid rgba(255, 255, 255, 0.2);
}

.help-title {
color: white;
font-size: 16px;
font-weight: 600;
margin-bottom: 12px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}

.help-list {
list-style: none;
padding: 0;
margin: 0;
}

.help-list li {
color: rgba(255, 255, 255, 0.9);
font-size: 14px;
line-height: 1.6;
margin-bottom: 6px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}

/* 响应式设计 */
@media (max-width: 768px) {
.icon-grid {
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
gap: 20px;
padding: 15px;
}
 
.icon-bg {
width: 60px;
height: 60px;
}
 
.icon-bg i {
font-size: 24px;
}
 
.controls {
flex-direction: column;
align-items: center;
}
 
.help-panel {
margin: 30px 10px 0;
padding: 15px;
}
}

/* 优化抖动效果在不同设备上的表现 */
@media (prefers-reduced-motion: reduce) {
.shaking,
.shaking .icon-bg,
.shaking .delete-btn,
.shaking .drag-indicator {
animation: none;
}
 
.shaking .icon-bg {
transform: scale(1.02);
}
}

/* 拖拽时的光标样式 */
.icon-item[draggable="true"] {
cursor: grab;
}

.icon-item[draggable="true"]:active {
cursor: grabbing;
}
</style>
posted @ 2025-09-12 15:33  Action_swt  阅读(4)  评论(0)    收藏  举报