Skip to content

Instantly share code, notes, and snippets.

@Rabbitzzc
Created December 23, 2020 13:09
Show Gist options
  • Save Rabbitzzc/e7c7dc7304f4d67a1d62b60c8a293e2a to your computer and use it in GitHub Desktop.
Save Rabbitzzc/e7c7dc7304f4d67a1d62b60c8a293e2a to your computer and use it in GitHub Desktop.
移动端搜索组件
<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