Created
December 23, 2020 13:09
-
-
Save Rabbitzzc/e7c7dc7304f4d67a1d62b60c8a293e2a to your computer and use it in GitHub Desktop.
移动端搜索组件
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div class="search-dialog"> | |
<!-- 移动端单组件展示 --> | |
<div | |
class="search-dialog-trigger" | |
:class="loaded == true ? 'hidden' : ''" | |
@click="showDialog" | |
> | |
<div | |
ref="thumb" | |
class="input-wrapper" | |
> | |
<div class="input"> | |
<i class="icon-search"></i> | |
<input :placeholder="palceholder" class="palceholder" disabled/> | |
</div> | |
</div> | |
</div> | |
<!-- 点击假象的搜索框后,动画展示 --> | |
<transition | |
:name="hasAnimation ? 'dialog' : ''" | |
@enter="enter" | |
@leave="leave" | |
> | |
<div | |
class="search-dialog-background" | |
v-show="appearedDialog" | |
v-scroll-lock="appearedDialog" | |
ref="dialog" | |
> | |
<div | |
ref="animate" | |
class="input-wrapper search-dialog-animate input-wrapper-real" | |
> | |
<div class="input"> | |
<i class="icon-search"></i> | |
<input :placeholder="palceholder" type="search" ref="realInput" v-model="keyword" class="palceholder"/> | |
</div> | |
<div | |
ref="cancleBtn" | |
@click="hideDialog" | |
class="cancle-btn" | |
>取消</div> | |
</div> | |
<div | |
ref="full" | |
v-show="slotVisiable" | |
@load="onLoadFull" | |
class="search-dialog-full" | |
> | |
</div> | |
<div | |
:class="slotVisiable?'visible':''" | |
class="slot-content" | |
ref="slotContent" | |
> | |
<slot> | |
<!-- 譬如支付宝,点击以后,下方覆盖全页面的内容就是那个玩意 --> | |
<!-- 奶奶个熊 --> | |
</slot> | |
</div> | |
</div> | |
</transition> | |
</div> | |
</template> | |
<script> | |
export default { | |
props: { | |
palceholder: { | |
type: String, | |
default: '搜索' | |
}, | |
// 是否开启动画 | |
hasAnimation: { | |
type: Boolean, | |
default: true | |
}, | |
// 动画延迟时间,暂时无用 | |
duration: { | |
duration: 500 | |
} | |
}, | |
data () { | |
return { | |
loaded: false, // 表示是否已经点击 | |
slotVisiable: false, | |
appearedDialog: false, | |
// 输入框的大小 | |
fullWidth: 0, | |
fullHeight: 0, | |
keyword: '', | |
showCancle: false | |
}; | |
}, | |
watch: { | |
keyword (value) { | |
this.$emit('change', value) | |
} | |
}, | |
created () { | |
this.fullWidth = window.screen.width; | |
this.fullHeight = window.screen.height; | |
}, | |
methods: { | |
// 获取原生尺寸 | |
getEleNaturalDimensions (el, callback) { | |
let nWidth, nHeight; | |
nWidth = el.offsetWidth; | |
nHeight = el.offsetHeight; | |
callback({ w: nWidth, h: nHeight }); | |
}, | |
// 展示 dialog 层 | |
showDialog () { | |
const thumb = this.$refs.thumb; | |
this.getEleNaturalDimensions(thumb, (dimension) => { | |
if (dimension.w > dimension.h) { | |
this.fullHeight = (dimension.h / dimension.w) * this.fullWidth; | |
} else { | |
this.fullWidth = (dimension.w / dimension.h) * this.fullHeight; | |
} | |
}); | |
this.appearedDialog = true; | |
this.$emit('open') | |
}, | |
// 取消按钮 | |
hideDialog () { | |
this.keyword = '' | |
this.appearedDialog = false; | |
this.$emit('close') | |
}, | |
enter () { | |
this.setStart(this.$refs.thumb); | |
this.animateImage(this.$refs.thumb, this.$refs.full, 1); | |
this.loaded = true; | |
this.slotVisiable = true; | |
}, | |
leave () { | |
this.animateImage(this.$refs.animate, this.$refs.thumb, 0); | |
this.slotVisiable = false; | |
this.loaded = false; | |
}, | |
onLoadFull () { | |
this.loaded = true; | |
}, | |
/** | |
* startEl 起始元素位置 | |
* destEl 目标元素位置 | |
* cancleEl 取消按钮 | |
* type 表示进入还是退出 | |
*/ | |
animateImage (startEl, destEl, type) { | |
const start = this.getBoundForDialog(startEl); | |
this.$nextTick(() => { | |
const dest = this.getBoundForDialog(destEl); | |
this.setDestination(start, { | |
top: dest.top, | |
left: dest.left, | |
width: dest.width || this.fullWidth, | |
height: dest.height || this.fullHeight | |
}, type); | |
this.setCancel(type) | |
type === 1 ? this.$refs.realInput.focus() : null | |
}); | |
}, | |
getBoundForDialog (el) { | |
const bound = el.getBoundingClientRect(); | |
const dialog = this.$refs.dialog; | |
return { | |
top: bound.top + dialog.scrollTop, | |
left: bound.left + dialog.scrollLeft, | |
width: bound.width, | |
height: bound.height | |
}; | |
}, | |
// 复制当前输入框的位置 | |
setStart (startEl) { | |
const start = this.getBoundForDialog(startEl); | |
const el = this.$refs.animate; | |
el.style.left = start.left + 'px'; | |
el.style.top = start.top + 'px'; | |
// 如果body设置了啥玩意,会导致整体的展示存在问题。 | |
el.style.width = '100%'; | |
// el.style.width = start.width + 'px'; | |
el.style.height = start.height + 'px'; | |
el.style.transitionDuration = '0s'; | |
el.style.transform = ''; | |
}, | |
// 设置动画目标路径 | |
setDestination (start, dest, type) { | |
const el = this.$refs.animate; | |
el.style.transitionDuration = ''; | |
// 如果是点击展示,则需要对 width 进行处理, | |
const translate = `translate(${dest.left - start.left}px, ${dest.top - start.top}px)`; | |
// 测试了一下, scale 会让整体的字体和 icon 呈现 scale 效果,比较突然 | |
// const scale = `scale(${dest.width / start.width}, ${dest.height / start.height})`; | |
// el.style.transform = `${translate} ${scale}`; | |
el.style.transform = `${translate}`; | |
}, | |
setCancel (type) { | |
// 取消按钮的宽度配置 | |
const cancleBtn = this.$refs.cancleBtn; | |
if (type === 1) { | |
cancleBtn.style.flexBasis = '24px'; | |
cancleBtn.style.margin = '0 4px 0 10px'; | |
} else { | |
cancleBtn.style.flexBasis = '0' | |
cancleBtn.style.margin = '0'; | |
} | |
} | |
} | |
}; | |
</script> | |
<style lang='scss' scoped> | |
$time: 300ms; | |
.search-dialog { | |
width: 100%; | |
.search-dialog-trigger { | |
width: 100%; | |
&.hidden { | |
opacity: 0; | |
} | |
} | |
.slot-content { | |
opacity: 0; | |
margin-top: 70px; | |
&.visible { | |
opacity: 1; | |
transition: opacity 1s ease $time; // 顺序很重要,不能放在 slot-content 上 | |
} | |
} | |
.search-dialog-animate { | |
display: flex; | |
position: absolute; | |
transform-origin: left top; | |
.cancle-btn { | |
display: flex; | |
align-items: center; | |
font-size: 12px; | |
color: #0080ff; | |
// 默认并不展示 | |
flex-basis: 0; | |
overflow: hidden; | |
padding: 0; | |
white-space:nowrap; // 对于取消,字体不能换行 | |
} | |
} | |
.input-wrapper { | |
padding: 12px 8px; | |
box-sizing: border-box; | |
.input { | |
border: none; | |
outline: none; | |
padding: 5px; | |
font-size: 12px; | |
border-radius: 5px; | |
height: 33px; | |
display: flex; | |
align-items: center; | |
flex: 1; | |
background: #fff; | |
border-radius: 8px; | |
input { | |
border: none; | |
outline: none; | |
padding: 5px; | |
font-size: 12px; | |
border-radius: 5px; | |
padding-left: 0; | |
flex: 1; | |
} | |
.icon-search { | |
color: #999; | |
margin-left: 8px; | |
margin-right: 8px; | |
font-size: 15px; | |
padding-top: 0px; | |
} | |
.icon-error-circle { | |
font-size:15px; | |
} | |
.palceholder { | |
color: #999; | |
background: #fff; | |
} | |
} | |
&-real { | |
.input { | |
background: #f2f4f5; | |
input { | |
caret-color: #0080ff; // 光标颜色调整,类似于大象首页 | |
} | |
.palceholder { | |
color: #999; | |
background: #f2f4f5; | |
} | |
} | |
} | |
} | |
} | |
.search-dialog-trigger { | |
margin: 0; | |
padding: 0; | |
background: none; | |
border: none; | |
cursor: pointer; | |
} | |
.search-dialog-background { | |
overflow: auto; | |
position: fixed; | |
top: 0; | |
right: 0; | |
left: 0; | |
bottom: 0; | |
width: 100%; | |
height: 100vh; | |
background-color: #fff; | |
text-align: center; | |
z-index: 999; | |
} | |
.dialog-enter-active, | |
.dialog-leave-active { | |
transition: background-color $time linear; | |
} | |
// 进入离开瞬间,需要调整背景颜色 - 是否可以抽离出主题色呢?? | |
.dialog-enter, | |
.dialog-leave-to { | |
background-color: #fff; | |
} | |
// 配置定位位置的动画,移动过程动画 | |
// $cubicValue: cubic-bezier(1, 0, 0.7, 1); 配置有点顿挫 | |
.dialog-enter-active .search-dialog-animate, | |
.dialog-leave-active .search-dialog-animate { | |
transition: transform $time linear; | |
} | |
// flex-basis + padding | |
.dialog-enter-active .search-dialog-animate .cancle-btn, | |
.dialog-leave-active .search-dialog-animate .cancle-btn { | |
transition: all $time linear; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment