目的
在 Taro Js 项目中使用了 Swiper 滑块容器组件,在微信小程序上查看时,发现高度总是固定的 150px。这很难受,我希望它能够自动适应内容的高度。
固定高度 | 自动适应 |
---|---|
失败的尝试
尝试用了各种 CSS 最终可以在网页端成功自适应,但是在微信小程序中就是不行。然后又按照文档 https://taro-docs.jd.com/docs/components/viewContainer/swiper 中提到的 adjustHeight进行了设置:
发现在微信小程序上仍然无效。继续看了一下文档,发现原来这个属性只有支付宝小程序支持!
谁的锅?
微信小程序。直觉上是微信团队在实现小程序时,做得比较粗糙,要求使用者传入一个固定的高度值,简单直接。因为支付宝在实现小程序时可以做到高度自适应,所以技术上应该是可行的。
尽管如此,Taro Js 难道不应该包一层吗?不仅仅可以打包到不同的平台,而是真正的统一开发体验。对于同样的需求,只需要一个标准的写法就好,而不是为不同的平台写不同的代码。
自己动手,虽丑但能用
目前自己封装了一个丑的实现,暂时比较满意,效果可以查看 https://taro.jefftian.dev/ 或者本文开头的效果对比图。
首先,因为支付宝小程序内置自适应,所以会在支付宝环境中直接返回原生的 Swiper 组件。然后,在网页环境中,使用纯 CSS 实现自适应。最后,检测到是微信小程序环境的话,还需要增加一个 JS 代码动态计算子元素的高度,并以固定的像素值设置到 Swiper 上。
使用方法
命名为 HardwaySwiper,使用上和原来的 Swiper 使用方式一致。比如:
plain export const Banner = () => <HardwaySwiper indicatorColor=#999 indicatorActiveColor=#333 circular indicatorDots autoplay full displayMultipleItems={1} adjustHeight=highest style={{ height: 300px }}
<SwiperItem className=banner-item onClick={() => Taro.navigateTo({ url: /pages/subpages/react-view/webview?src=${encodeURIComponent(https://mp.weixin.qq.com/s/nwUTbYfi7SMh2X5Lcn-YpA)} })}
<Image
src={https://uniheart.pa-ca.me/proxy?url=${encodeURIComponent(https://raw.githubusercontent.com/Jeff-Tian/wechat-oauth-ts/master/img_1.png)}}
mode=widthFix
/>
实现细节
组件代码
最复杂的就是在 useEffect 中计算和设置高度值了:
plain import { Swiper } from @tarojs/components; import Taro from @tarojs/taro; import React, { useEffect, useRef, useState } from react;
import ./hardway-swiper.styl;
const AutoAdjustHeightSwiper = ({ children, ...otherProps }) => { const [currentIndex, setCurrentIndex] = useState(0); const [swiperHeight, setSwiperHeight] = useState(auto); const swiperRef = useRef();
const adjustSwiperHeight = () => { const query = Taro.createSelectorQuery(); query.selectAll(.swiper-item.x).boundingClientRect(); query.exec((res) => { console.log(res = , res); const heights = res[0].map((item) => item.height); const maxHeight = Math.max(...heights); setSwiperHeight(${maxHeight}px); }); };
useEffect(() => { adjustSwiperHeight(); }, [currentIndex]);
const handleSwiperChange = (e) => { setCurrentIndex(e.detail.current); };
const itemsWithRef = React.Children.map(children, (child) => { return React.cloneElement(child, { className: swiper-item x }); });
return <Swiper className={hardway-swiper} style={{ height: swiperHeight }} onChange={handleSwiperChange} ref={swiperRef} {...otherProps}> {itemsWithRef} ; };
const HardwaySwiper = ({ children, ...otherProps }) => { if (Taro.getEnv() === Taro.ENV_TYPE.ALIPAY) { return <Swiper className={hardway-swiper} {...otherProps}> {children} ; }
if (Taro.getEnv() === Taro.ENV_TYPE.WEB) { return <Swiper style={{ height: 100% }} className={hardway-swiper} {...otherProps}> {children} ; }
return <AutoAdjustHeightSwiper className={hardway-swiper} {...otherProps}> {children} ; };
export default HardwaySwiper;
CSS 样式
plain .hardway-swiper { margin-top: 5px overflow visible
.swiper-item, swiper-item { overflow visible height auto !important text-align center display flex align-items center justify-content center } }