uniApp(H5,App端) 实现手指下拉屏幕,顶部背景图片放大,松开缩放还原效果。

最近写项目要用到类似抖音的我的页面,在顶部用户执行下拉操作时顶部背景图会放大并高度增加的效果,使页面感觉更加的丝滑。先上效果:

文章末尾有完整的代码,着急用的可以直接跳转的文章最后。

2022.5.24 因为性能问题,又在最下边重新优化的最新代码,但是思路还是不变的。

上代码之前先了解几个事件:

@touchstart 当用户手指按下时

@touchmove 当用户手指移动过程中

@touchend 当用户手指抬起时

这几个事件会记录用户手指在屏幕和页面上的位置信息,还有一个uniapp的生命周期事件onPageScroll()监听页面滚动。

        !!!这几个事件一定要绑定在最外层的标签上,监听整个页面!!!

这个功能的实现思路大概是:

第一步:先监听到当前位置是不是页面视口的顶部onPageScroll(),再监听用户手指的动作。

第二步:当视口位于页面的顶部并且用户做了下拉动作,则动态的修改顶部背景图的高度,宽度(放大效果)。

第三步:当用户手指抬起时@touchend,将图片恢复到原来到宽高。

        按照我们的思路一步一步用代码实现:

第一步:先监听到当前位置是不是页面视口的顶部onPageScroll(),再监听用户手指的动作。

<view class="my"  @touchmove="move" @touchend="end">
  //绑定监听事件,当用户手指移动时和用户手指抬起时
</view>

<script>
export default {
    data() {
	  return {
        // 用户操作
		clientData: {
		  // 用户手指Y轴
		  clientMoveY: '', //按下
		  clientEndY: '', //抬起
		  // 页面Y轴
		  pageY: ''
		}
      }
    },
    onPageScroll(e) {
      //获取当前页面滚动高度
	  this.clientData.pageY = e.scrollTop;
	},
    methods: {
	  // 手指移动
	  move(e) {
	  },
	  // 手指抬起
	  end(e) {
	  },
	}
}
</script>

第二步:当视口位于页面的顶部并且用户做了下拉动作,则动态的修改顶部背景图的高度,宽度。(我这里图片给图片放大用的是transform:scale()

onPageScroll(e) {
  //获取当前页面滚动高度
  this.clientData.pageY = e.scrollTop;
},
methods: {
  // 手指移动
  move(e) {
	// 先判断用户是否到达了顶部
	if (this.clientData.pageY < 1) {
	  // 本次手指移动的位置和上次移动的位置对比 <1 证明用户在下拉
	  if (this.clientData.clientMoveY - e.changedTouches[0].clientY < 1) {
	    // 拿到下拉的距离
		let distance = this.clientData.clientMoveY - e.changedTouches[0].clientY;
		// 最大下拉到800rpx ,放大1.8倍大小
		if (this.sheight < 800 && this.imgWidth < 1.8) {
		  this.sheight -= distance;
		  this.imgWidth -= distance / 1000;
		} else {
		  //超过800或者1.8倍就return
		  return;
		}
	  }
	  // 上边的代码执行结束之后再把本次手指的位置赋值给data中,用来下一次对比
	  this.clientData.clientMoveY = e.changedTouches[0].clientY;
	}
  },
  // 手指抬起
  end(e) {
	// this.clientData.clientEndY = e.changedTouches[0].clientY;
	// 当图片的高度大于400的时候手指抬起再调用函数
	if (this.sheight > 400) {
	  // 执行动画
	  this.isPlay = true;
	  // 动画结束后把顶部图片的高度和放大比例该会原来的值
	  setTimeout(() => {
		this.sheight = 400;
		this.imgWidth = 1;
		// 并把动画的class名取消掉
		this.isPlay = false;
	  }, 504);
	}
  },
}

第三步:当用户手指抬起时@touchend,将图片恢复到原来到宽高。

这里我们需要先定义一个动画,当用户手指抬起是执行这个动画:

// 一个动画
.isPlay {
  animation: big 0.5s 1 alternate ease-in-out forwards;
}
@keyframes big {
  100% {
  transform: scale(1);//图片放大比例恢复为1
	height: 400rpx;//高度恢复为原来的高度
  }
}

这个isPlay的class名需要动态的绑定给image,执行动画时绑定上,执行动画,当动画执行完,接触,等待下一次触发事件。

end(e) {
  // 当图片的高度大于400的时候手指抬起再调用函数
  if (this.sheight > 400) {
	// 执行动画
	this.isPlay = true;
	// 动画结束后把顶部图片的高度和放大比例该会原来的值
	setTimeout(() => {
	  this.sheight = 400;
	  this.imgWidth = 1;
	  // 并把动画的class名取消掉
	  this.isPlay = false;
    }, 504);//我这里动画定义的是0.5s结束
  }
},

大概就是这么个思路,动态绑定和一些小的地方就一一写出来了。

这个效果的实现肯定有很多种,这只是其中的一种,比如图片放大效果,也可以直接更改图片的宽度来实现放大。

完整代码:

<template>
	<view class="my" @touchend="end" @touchmove="move">
		<!-- 顶部背景图 -->
		<view :class="{'banner':true,'isPlay':isPlay}" :style="{ height: sheight + 'rpx' }">
			<view class="">
				<image :class="{'isPlay':isPlay}" :style="{ height: sheight + 'rpx',transform:`scale(${imgWidth})`}" src="https://p4.music.126.net/nILBk4DaE3yV__25uq-5GQ==/18641120139241412.jpg?param=640y300" mode=""></image>
			</view>
		</view>
	</view>
</template>

<script>
export default {
	components: {
	},
	data() {
		return {
			sheight: 400,//高度
			imgWidth:1,//放大比例
			isPlay:false,//是否播放动画
			// 用户滑动
			clientData: {
				// 用户手指Y轴
				clientMoveY: '', //按下
				clientEndY: '', //抬起
				// 页面Y轴
				pageY: ''
			},
		};
	},
	onLoad() {
	},
	onPageScroll(e) {
		//获取当前页面滚动高度
		this.clientData.pageY = e.scrollTop;
	},

	methods: {
		// 手指移动
		move(e) {
			// 先判断用户是否到达了顶部
			if (this.clientData.pageY < 1) {
				// 本次手指移动的位置和上次移动的位置对比 <1 证明用户在下拉
				if(this.clientData.clientMoveY - e.changedTouches[0].clientY < 1) {
					// 拿到下拉的距离
					let distance = this.clientData.clientMoveY - e.changedTouches[0].clientY;
					// 最大下拉到800rpx ,放大1.8倍大小
					if(this.sheight < 800 && this.imgWidth < 1.8){
						this.sheight -= distance
						this.imgWidth -= distance/500
					}else{//超过800或者1.8倍就return 优化性能
						return
					}
				}else{
					return
				}
				// 上边的代码执行结束之后再把本次手指的位置赋值给data中,用来下一次对比
				this.clientData.clientMoveY = e.changedTouches[0].clientY;
			}
		},
		// 手指抬起
		end(e) {
			this.clientData.clientEndY = e.changedTouches[0].clientY;
			// 当图片的高度大于400的时候手指抬起再调用函数
			if(this.sheight > 400){
				// 执行动画
				this.isPlay = true
				// 动画结束后把顶部图片的高度和放大比例该会原来的值
				setTimeout(()=>{
						this.sheight=400
						this.imgWidth=1
						// 并把动画的class名取消掉
						this.isPlay = false
				},504)
			}
		}
	}
};
</script>

<style lang="scss" scoped>
.banner {
	width: 100vw;
	overflow: hidden;//防止图片放大,宽度被撑开
	background-size: cover;
	position: relative;
	image {
		width: 100%;
		max-height: 800rpx;
		position: absolute;
		bottom: 0;
	}
}
// 一个动画
.isPlay{
	animation: big 0.5s 1 alternate ease-in-out forwards;
}
@keyframes big{
	100% {
		transform: scale(1);
		height: 400rpx;
	}
}
</style>

代码中有任何建议,可以评论下来,一起成长,加油!

感觉有用的小伙伴也可以关注我,后期在开发过程中遇到有趣的功能都会同步到博客上。

<template>
  <view
    class="my"
    @touchstart="coverTouchstart"
    @touchmove="coverTouchmove"
    @touchend="coverTouchend"
  >
  <!-- 顶部背景图 -->
    <view
      class="banner"
      :style="{
        height: sheight + 'rpx',
        transform: coverTransform,
        transition: coverTransition,
      }"
    >
      <view class="">
         <image
           style="height: 100%"
           :src="this.form.backgroundPath"
           mode="aspectFill"
         ></image>
      </view>
    </view>
  </view>
</template>
<script>
let startY = 0,//手指按下时屏幕的位置
  moveY = 0,//手指当前在屏幕中的位置
  pageAtTop = true;//是否到顶
export default {
  data() {
    return {
      coverTransform: "scale(1)", //放大倍数
      coverTransition: "0s", //过度时间
      moving: false,
      form: {},
      sheight: 280, //高
    };
  },
  methods: {
    coverTouchstart(e) {
	  if (pageAtTop === false) {
		return;
	  }
	  this.coverTransition = '.1s ease-out';
		startY = e.touches[0].clientY;
    },
	coverTouchmove(e) {
	  moveY = e.touches[0].clientY;
	  let moveDistance = moveY - startY;
	  if (moveDistance < 0) {
		this.moving = false;
		return;
	  }
	  this.moving = true;
	  if (moveDistance > 0) {
	    if (this.sheight > 800) return;
	    this.sheight = 280 + moveDistance;
	    this.coverTransform = 'scale(1.08)';
	    this.coverTransition = '.1s ease-out';
	  }
	},
	coverTouchend() {
	  if (this.moving === false) {
		return;
	  }
	  this.moving = false;
	  this.sheight = 280;
	  this.coverTransform = 'scale(1)';
	  this.coverTransition = '.2s ease-out';
	},
  }
}
</script>
<style lang="scss" scoped>
  .banner {
    width: 100vw;
    overflow: hidden;
    background-size: cover;
    position: relative;
  
    image {
      width: 100%;
      position: absolute;
      bottom: 0;
    }
  }
</style>

  • 9
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
Uniapp实现微信分享,可以通过以下步骤进行: 1.在 manifest.json 文件中配置微信分享所需的权限: ``` { "mp-weixin": { "appid": "你的微信公众号appid", "permission": { "scope.userLocation": { "desc": "你的位置信息将用于小程序位置接口的效果展示" }, "scope.userInfo": { "desc": "你的用户信息将用于小程序内部展示" }, "scope.record": { "desc": "你的录音功能将用于小程序内部展示" }, "scope.writePhotosAlbum": { "desc": "你的相册将用于小程序内部展示" }, "scope.camera": { "desc": "你的摄像头将用于小程序内部展示" }, "scope.userLocationBackground": { "desc": "你的位置信息将用于小程序位置接口的效果展示" }, "scope.werun": { "desc": "你的微信运动数据将用于小程序展示" }, "scope.invoice": { "desc": "你的发票信息将用于小程序展示" }, "scope.invoiceTitle": { "desc": "你的发票抬头信息将用于小程序展示" }, "scope.record": { "desc": "你的录音功能将用于小程序展示" }, "scope.writePhotosAlbum": { "desc": "你的相册将用于小程序展示" } }, "window": { "navigationBarBackgroundColor": "#ffffff", "navigationBarTitleText": "uni-app", "navigationBarTextStyle": "black", "backgroundTextStyle": "dark", "backgroundColor": "#F7F7F7" }, "networkTimeout": { "request": 60000, "downloadFile": 60000 }, "tabBar": { "selectedColor": "#007AFF", "backgroundColor": "#ffffff", "color": "#999999", "borderStyle": "black", "list": [ { "pagePath": "pages/index/index", "text": "首页", "iconPath": "static/tabbar/home.png", "selectedIconPath": "static/tabbar/home-active.png" }, { "pagePath": "pages/my/my", "text": "我的", "iconPath": "static/tabbar/my.png", "selectedIconPath": "static/tabbar/my-active.png" } ] }, "plugins": { "myPlugin": { "version": "1.0.0", "provider": "wxidabcdefg" } }, "preloadRule": { "async": "networkFirst", "sync": [ "pages/index/index" ], "custom": { "name": "my-custom-name", "path": "path/to/my/custom-module" } }, "sitemapLocation": "sitemap.json", "debug": true, "workers": { "path": "static/js/worker.js", "clear": true }, "requiredBackgroundModes": [ "audio", "download", "location", "fetch", "video" ], "navigateToMiniProgramAppIdList": [ "wx1234567890abcdef", "wx0987654321zyxwvu" ], "usingComponents": { "my-custom-component": "/static/my-custom-component" }, "permission": { "scope.userLocation": { "desc": "你的位置信息将用于小程序位置接口的效果展示" }, "scope.userInfo": { "desc": "你的用户信息将用于小程序内部展示" }, "scope.record": { "desc": "你的录音功能将用于小程序内部展示" }, "scope.writePhotosAlbum": { "desc": "你的相册将用于小程序内部展示" }, "scope.camera": { "desc": "你的摄像头将用于小程序内部展示" }, "scope.userLocationBackground": { "desc": "你的位置信息将用于小程序位置接口的效果展示" }, "scope.werun": { "desc": "你的微信运动数据将用于小程序展示" }, "scope.invoice": { "desc": "你的发票信息将用于小程序展示" }, "scope.invoiceTitle": { "desc": "你的发票抬头信息将用于小程序展示" }, "scope.record": { "desc": "你的录音功能将用于小程序展示" }, "scope.writePhotosAlbum": { "desc": "你的相册将用于小程序展示" } } } } ``` 2.在需要分享的页面中调用微信分享的 API: ``` export default { onShareAppMessage() { return { title: '分享标题', path: '/pages/index/index', imageUrl: 'http://example.com/share.png' } } } ``` 其中,`title` 表示分享的标题,`path` 表示分享的路径,`imageUrl` 表示分享的图片链接。根据需要进行修改即可。 3.在微信公众平台中配置网页授权域名和JS接口安全域名。 以上就是实现 Uniapp h5微信分享的步骤。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值