美文网首页APP & program
微信小程序组件: 点击列表项弹出输入框

微信小程序组件: 点击列表项弹出输入框

作者: 牛会骑自行车 | 来源:发表于2023-01-30 13:30 被阅读0次

注意事项

使用页面设置
  1. 每个元素需要设置id, 便于后面进行取值(单项底边界)
  2. 如果是有tabBar的主页面, 需要传参告诉组件, 进行tabBar的隐藏设置(不隐藏的话textarea的fixed定位会将tabBar的距离顶出来.....说不清楚了, 请看图片😂)
组件
  1. 页面滚动容器的paddingBottom需要通过triggerEvent带着数值传出来
  2. textarea中webkit-scrollbar的消失, 需要将样式写在app.wxss中(组件的wxss中只能使用类名) 报错
  3. wx.pageScroll可以在组件中使用

代码

组件wxml ⬇️

<view class="comment-view" id="njxCommentTextarea" style="bottom: {{textBottom}}px;">
  <textarea class="textarea" style="margin-right: {{isIos ? 0 : 10}}px;padding: {{isIos ? '0 10px' : '4px 10px'}};" value="{{inputValue}}" placeholder="{{placeholder}}" placeholder-class="textarea-placeholder" focus="{{focus}}" auto-height fixed adjust-position="{{false}}" cursor-spacing="140" show-confirm-bar="{{false}}" confirm-type="send" bindkeyboardheightchange="getKeyBoardHeight" bindblur="onBlur" bindinput="onInput" bindconfirm="onSend"></textarea>
  <view class="njx_button" wx:if="{{!isIos}}" catchtap="onSend">发送</view>
</view>

组件wxss ⬇️

@import "/components/common.wxss";

.comment-view {
  width: 100vw;
  padding: 6px 12px;
  display: flex;
  justify-content: space-between;
  align-items: center;

  font-size: 14px;

  background: #ededf4;
  position: fixed;
}

.textarea {
  width: 100%;
  background: #fff;
  border-radius: 4px;
}

.textarea-placeholder {
  color: #666666;
}
.njx_button {
  min-width: 54px!important;
}

组件js ⬇️

Component({
  properties: {
    inputValue: String,
    // 滚动容器id(必传参): 用来获取当前scrollTop
    scrollContainerId: {
      required: true,
      type: String,
      value: "",
    },
    // 当前点击元素的id(必传参)
    currentElId: {
      required: true,
      type: String,
      value: "",
    },
    placeholder: String,
    // 是否是有tabBar的页面: 如果有, 需要在输入框弹出且定位的过程中将tabBar隐藏
    hasTabBar: {
      type: Boolean,
      value: false,
    },
  },
  data: {
    isIos: false,
    focus: false,

    initPageHeight: false,
    pageHeight: 0,
    initKeyBoardHeight: false,
    keyBoardHeight: 0,
    initTextHeight: false,
    textHeight: 0,

    textBottom: -1000,
    elBottom: 0,

    beforeScrollTop: 0,
  },
  methods: {
    async getSystem() {
      const {
        system,
      } = wx.getSystemInfoSync();

      this.setData({
        isIos: system.includes('iOS'),
      })
    },
    getDomInfo({
      elId,
      isComponent
    }) {
      // createSelectorQuery用在组件中的元素时用this, 页面中的元素时使用wx
      let _this = isComponent ? this : wx;
      return new Promise((resolve) => {
        _this.createSelectorQuery().select('#' + elId).boundingClientRect().exec(function (res) {
          resolve(res[0])
        })
      })
    },
    getKeyBoardHeight(e) {
      const {
        height: keyBoardHeight,
      } = e.detail;

      if (!this.data.initKeyBoardHeight && keyBoardHeight !== 0) {
        this.setData({
          keyBoardHeight,
          initKeyBoardHeight: true,
        })

        this.fixTextarea();
      }
    },
    onPageScroll({
      scrollTop,
      paddingBottom
    }) {
      this.triggerEvent('padpage', {
        paddingBottom,
      })
      
      wx.pageScrollTo({
        scrollTop,
      })
    },

    async setEl(elId) {
      wx.hideTabBar();
      // 获取页面高度
      if(!this.data.initPageHeight) {
        const {screenHeight, windowHeight} = wx.getSystemInfoSync();

        this.setData({
          pageHeight: this.data.hasTabBar ? screenHeight : windowHeight
        })
      }
      // 获取元素底边距
      const {
        bottom: elBottom
      } = await this.getDomInfo({
        elId
      });

      if (!this.data.initTextHeight) {
        const {
          height: textHeight
        } = await this.getDomInfo({
          elId: 'njxCommentTextarea',
          isComponent: true
        });

        this.setData({
          textHeight,
          initTextHeight: true,
        })
      }
      // 聚焦
      this.setData({
        focus: true,
        elBottom,
      })
      this.data.keyBoardHeight && this.fixTextarea();
    },
    fixTextarea() {
      let textBottom = this.data.pageHeight - this.data.elBottom - this.data.textHeight;

      let fixPosition = ({
        value,
        beforeScrollTop
      }) => {
        this.setData({
          textBottom: this.data.isIos ? value : value - 1,
          beforeScrollTop
        })
      }

      if (textBottom > this.data.keyBoardHeight) {
        fixPosition({
          value: this.data.isIos ? this.data.keyBoardHeight : this.data.keyBoardHeight - 1,
          beforeScrollTop: 0
        });
      } else {
        wx.createSelectorQuery().select('#' + this.data.scrollContainerId).boundingClientRect().selectViewport().scrollOffset().exec(res => {
          const beforeScrollTop = res[1].scrollTop;
          fixPosition({
            value: this.data.keyBoardHeight,
            beforeScrollTop
          });

          const scrollTop = beforeScrollTop + this.data.keyBoardHeight - textBottom;

          this.onPageScroll({
            paddingBottom: this.data.textHeight + this.data.keyBoardHeight,
            scrollTop,
          });
        })
      }
    },
    onBlur() {
      this.onPageScroll({
        scrollTop: this.data.beforeScrollTop,
        paddingBottom: 0
      })
      this.data.hasTabBar && wx.showTabBar();
      this.setData({
        textBottom: -this.data.textHeight,
        inputValue: "",
      })
    },
    onInput(e) {
      const inputValue = e.detail.value;
      this.setData({
        inputValue,
      })
    },
    onSend(e) {
      const {
        value
      } = e.detail;

      this.triggerEvent('sendvalue', {
        value: value || this.data.inputValue,
      })
    },
  },
  observers: {
    currentElId(id) {
      id && this.data.textBottom < 0 && this.setEl(id);
    },
  },
  lifetimes: {
    attached() {
      this.getSystem();
    }
  }
})
使用页面代码

wxml ⬇️

<view class="njx_container h_100">
  <view id="scrollNjx" style="padding-bottom: {{paddingBottom}}px;">
    <view class="njx_card" wx:for="{{list}}" wx:key="id" id="id-{{item.id}}" bindtap="toCommentComponent" data-item="{{item}}">
      {{item.id}}
    </view>
  </view>
</view>

<njx-comment-input scrollContainerId="scrollNjx" currentElId="{{currentElId}}" placeholder="{{placeholder}}" hasTabBar bindsendvalue="onSend" bindpadpage="onPadPage"></njx-comment-input>

js ⬇️

let list = [];
for (let i = 0; i <= 20; i++) {
  list.push({
    id: i
  });
}

Page({
  data: {
    list,
    placeholder: "",
    paddingBottom: 20,
    currentElId: "",
  },

  toCommentComponent(e) {
    const {item} = e.currentTarget.dataset;
    this.setData({
      currentElId: 'id-' + item.id,
      placeholder: '我的评论ID是 ---- ' + item.id,
    })
  },
  onSend(e) {
    const {value} = e.detail;
    console.log(value, 'VALUE ~~~~~~~~~~~~~~~');
  },
  onPadPage(e) {
    const {paddingBottom} = e.detail;
    this.setData({
      paddingBottom,
    })
  },
  onLoad(options) {
  },
})
最后记得在json中那啥usingComponents呀~~

这篇注释有点少, 都在之前那篇里, 有变化的才写了新的嘿嘿

tada~~~一个评论区的输入框组件就完成啦💃

实际使用时发现了新的问题, 这里的代码懒得改了😂
最好将输入框容器的id也设置为动态, 从使用页面传进来.

相关文章

网友评论

    本文标题:微信小程序组件: 点击列表项弹出输入框

    本文链接:https://www.haomeiwen.com/subject/wbayhdtx.html