• 微信小程序 — 自定义picker选择器弹窗内容+textarea穿透bug


    微信小程序中定义好的几种picker选择器,不管是日期选择器还是地区选择器,或是其他的都只有定死的样式和内容。

    但是大多数开发程序的情况下还是需要自己写样式的,或是内容的。

    例如:

    代码如下:

      <view class="free-btns" style="margin-top: 10vh;background:none;">
        <button class="free-btn" bindtap="toggleDialog">
          选定国家:{{value}}
        </button>
      </view>
    
      <view class="free-dialog {{ showDialog ? 'free-dialog--show' : '' }}">
        <view class="free-dialog__mask" bindtap="toggleDialog" />
        <view class="free-dialog__container">
          <view style="padding: 5% 5% 15%;">
            <form bindsubmit='submit' bindreset="reset">
              <view bindtap='freetoBack' class="free-button free-dialog-reset">取消</view>
              <view bindtap='freeBack' class="free-button free-dialog-submit">确定</view>
    
              <radio-group class='free-radios' bindchange="radioChange">
                <label class="free-radio" bindtap="click" wx:for="{{items}}" wx:key="{{items}}" data-id="{{index}}" style="{{index==id?'background:#48c23d;color:#fff;':'background:#fff;color:#000;'}}">
                  <radio value="{{item.name}}" name="{{item.value}}"></radio>
                  <label class="free-text">{{item.value}}</label>
                </label>
              </radio-group>
            </form>
    
          </view>
        </view>
      </view>

    样式:

    .free-dialog__mask {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      z-index: 10;
      background: rgba(0, 0, 0, 0.7);
      display: none;
    }
    .free-dialog__container {
      position: fixed;
      left: 0;
      bottom: 0;
      width: 750rpx;
      background: white;
      transform: translateY(150%);
      transition: all 0.4s ease;
      z-index: 11;
    }
    .free-dialog--show .free-dialog__container {
      transform: translateY(0);
    }
    .free-dialog--show .free-dialog__mask {
      display: block;
    }
    /*模态框中的内容*/
    .free-button{
      display: inline-block;
      width:50px;
      text-align: center;
      font-size:20px;
      color:#707070;
      margin-bottom:20px;
    }
    .free-dialog-submit{
      float: right;
      color:#48c23d;
    }
    radio-group{
      margin:10rpx 0rpx;
    }
    radio-group>label{
      width:22.5%;
      display: inline-block;
      border:1px solid #ddd;
      padding:10px 0px;
      margin:0px 2px 2px;
    }
    
    radio-group label radio{
      width:100%;
      z-index: 3;
      display: none;
    }
    .checked{
      background:#48c23d;
      color:#fff;
    }
    radio-group label .free-text{
      width:100%;
      text-align: center;
      display: inline-block;
    }

    js:

    Page({
      data: {
        showDialog: false,
        items: [
          { name: '中国', value: '中国' },
          { name: '美国', value: '美国' },
          { name: '巴西', value: '巴西' },
          { name: '日本', value: '日本' },
          { name: '英国', value: '英国' },
          { name: '法国', value: '法国' },
          { name: '韩国', value: '韩国' },
          { name: '俄罗斯', value: '俄罗斯' },]
    
      },
      /*点击变色*/
      click:function(e){
        var id = e.currentTarget.dataset.id
        var that = this
        that.setData({
          id:id
        })
      },
      onLoad: function (options) {
        var that = this
        that.setData({
          value:'show'
        })
      },
      radioChange: function (e) {
        console.log('radio发生change事件,携带value值为:', e.detail.value)
        var that = this
        that.setData({
          value: e.detail.value
        })
        console.log(this.data.value)
      },
      toggleDialog() {
        this.setData({
          showDialog: !this.data.showDialog
        });
      },
      freeBack:function(){
        var that = this
        if(this.data.value=='show'){
          wx.showModal({
            title: '提示',
            content: '你没有选择任何内容',
          })
        }
        that.setData({
          showDialog: !this.data.showDialog
        })
      },
      freetoBack: function () {
        var that = this
        wx.showModal({
          title: '提示',
          content: '你没有选择任何内容',
        })
        that.setData({
          showDialog: !this.data.showDialog,
          value:'show',
          checked: false,
        })
      },
    })

    遇到个BUG,就是表单中有textarea组件的时候,弹出层设置z-index,在模拟器上运行没问题,但是真机就无效了,textarea的内容就会穿透出来。

    查了资料发现是官方的坑,textarea组件为原生组件层级最高,不能通过 z-index 控制层级。

    解决办法:

    1、隐藏 textarea:

    设置一个值,用wx:if 去控制textarea的显示和隐藏。就是说当弹窗弹出的时候,在textarea做wx:if判断那个值为false,把textarea隐藏,然后关闭弹窗或点击其他的时候,把那个值再设为true,显示textarea。当然hidden原理一样。

    代码如下:

    <view class='itemArea'>
              <view class="form_item">留言类型:</view>
              <view class="form_item flex_row jc_between underLine"> 
                <button class="free-btn" bindtap="toggleType">
                  {{typename}}
                </button>
                <view class="red" bindtap="toggleType">选择</view>
              </view>
    
              <view bindtap="hideView">
              <view class="free-dialog {{ showType ? 'free-dialog-show' : 'free-dialog_mask' }}">
                <view class="free-dialog_mask" bindtap="toggleType" />
                <view class="free-dialog_container1">
                  <view class='p25'>
                    <form bindsubmit='submit' bindreset="reset">
                      <radio-group class='free-radios' bindchange="radioChange2">
                        <label class="free-radio" bindtap="clickType" wx:for="{{msgtype}}" wx:key="{{msgtype}}" data-idn="{{index}}" data-name='{{item.typename}}' data-id='{{item.typeid}}' style="{{index==idn?'background:#FBDBDB;':'background:#F5F5F5;'}}">
                          <radio value="{{item.typename}}" name="{{item.typename}}"></radio>
                          <label class="free-text">{{item.typename}}</label>
                        </label>
                      </radio-group>
                    </form>
                  </view>
                </view>
                </view>
    
              </view>
          </view>
          <view class='pb20'>
              <view class="form_item">留言内容:</view>
              <view class="form_item">
                <textarea class='form_textarea' hidden="{{noTextarea}}" placeholder='请填写留言内容' bindinput='msgcontent' value="{{msgcontent}}" name="msgcontent"></textarea>
              </view>
           </view>

    js:

    data: {
        noTextarea: false, //textarea弹出层的时候隐藏
    },
    
    //点击其他位置隐藏遮罩层
      hideView:function(){
        var that = this;
        that.setData({
          noTextarea: false,
        })
     },
    
    //选择留言类型
      toggleType: function () {
        this.setData({
          showType: !this.data.showType,
          noTextarea: true,
        });
      },
      radioChange2: function (e) {
        var that = this;
        that.setData({
          typename: e.detail.value,
          showType: !this.data.showType,
          noTextarea: false,
        })
        console.log(this.data.typename);
      },
      clickType:function(e){
        var that = this;
        var idn = e.currentTarget.dataset.idn;
        var id = e.currentTarget.dataset.id;
        that.setData({
          idn: idn,
          typeid: id,
          noTextarea: false,
        })
        console.log(that.data.typeid);
     },

     2、还有一种解决方法是使用替代元素

    有时候, textarea穿透的不是遮罩层,或者遮罩层以一种半透明而非完全遮住页面内容的形式呈现,担心用户能够看到因为 textarea的消失而导致页面跳动,产生不好的用户体验,那么就可以使用替代元素来替代 textarea而非将之直接隐藏掉。

    基本的 textarea组件只接受文本的输入,抛开可输入性的话,外观上看就是一个含有文本节点的简单元素,只需要获取当前状态下的 textarea中输入的文字,将之赋予给一个样式与 textarea相同的普通元素,就达到了临时替代的效果。

    <!-- 这是真正的 textarea组件 -->
    <textarea id="text-area" value="{{txtRealContent}}" bindinput='txtInput' wx:if="{{!showMask}}" />
    <!-- 这是用于模拟 textarea的替代元素 -->
    <view class='rich-text' style="{{('height:' + txtHeight + 'px')}}" wx:else>
      <rich-text nodes="{{txtRealContent}}"></rich-text>
    </view>
    • 由于需要实时获取 textarea中已经输入的内容,所以给 textarea元素加了个 bindinput的监听器
    • showMask用于标识是否显示遮罩层(或者其他可能会被 textarea穿透的浮动元素),如果显示遮罩层,则隐藏 textarea元素,并显示替代原宿
    • 这里 textarea的隐藏使用了 wx:if,会使其彻底地从页面中消失,而重新显示出来的时候,textarea元素会重新创建,丢失原先输入,所以给其加了个 value属性,其值 txtRealContent就是缓存的 textarea已经输入的文本内容;如果你不用这种方法,不让 textarea完全显示,而仅仅是隐藏,例如使用 hidden="{{ showMask ? true :false }}",因为不涉及到 textarea的删除与重建,所以就无需添加 value属性来控制文本内容了。
    • textarea是可以输入可换行的文本内容的,所以这里使用了 rich-text组件,在使用的时候,我发现 rich-text好像不支持溢出隐藏,所以又额外在其外面包了一层 view组件,并将其高度设置为和 textarea相同。

    上面四个步骤,都比较简单,稍微需要注意的是,如果 textarea的内容包含了换行文本,则需要对换行符进行处理:

    textareaContent.replace(/
    /g, '<br/>')
    如果你想让 textarea自动增加高度而不是固定高度,给 textarea加了个 auto-height,那么就需要“实时”获取其高度 说是 “实时”,其实也并不是那么实时,不考虑其他样式的变化, textarea的高度与行数有关,每增减一行,其高度才会变化,所以只需要监控其内容行数的变化即可,恰好 textarea组件也已经提供了这个监控器:bindlinechange
    实例代码如下:
    <view class="page-body">
      <button bindtap="changeMaskVisible">切换mask</button>
      <view class="textarea-wrp">
        <textarea id="text-area" value="{{txtContent}}" bindinput='txtInput' bindlinechange="textAreaLineChange" wx:if="{{!showMask}}" auto-height />
        <view class='rich-text' style="{{('height:' + txtHeight + 'px')}}" wx:else>
          <rich-text nodes="{{txtRealContent}}"></rich-text>
        </view>
      </view>
      <button>Footer</button>
      <view wx:if="{{showMask}}" bindtap="changeMaskVisible" class="mask">
        <view class="mask-content"></view>    
      </view>
    </view>

    样式:

    .rich-text {
      overflow: hidden;
    }
    .mask {
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background-color: rgba(0, 0, 0, .6);
      z-index: 10;
    }
    .mask-content {
      position: fixed;
      top: 44%;
      left: 50%;
      height: 60%;
      width: 60%;
      transform: translate(-50%, -50%);
      background-color: yellowgreen;
      z-index: 12;
    }

    js:

    Page({
      data: {
        txtRealContent: '',
        txtContent: '',
        showMask: false,
        txtHeight: 0
      },
      textAreaLineChange(e) {
        this.setData({ txtHeight: e.detail.height })
      },
      txtInput(e) {
        this.setData({ txtContent: e.detail.value })
      },
      changeMaskVisible(e) {
        if (!this.data.showMask) {
          // 将换行符转换为wxml可识别的换行元素 <br/>
          const txtRealContent = this.data.txtContent.replace(/
    /g, '<br/>')
          this.setData({ txtRealContent })
        }
        this.setData({ showMask: !this.data.showMask })
      }
    })
  • 相关阅读:
    还原 | revert (Cascading & Inheritance)
    过滤器 | filter (Filter Effects)
    过渡时间 | transition-duration (Animations & Transitions)
    过渡延时 | transition-delay (Animations & Transitions)
    过渡属性 | transition-property (Animations & Transitions)
    过渡定时功能 | transition-timing-function (Animations & Transitions)
    过渡 | transition (Animations & Transitions)
    ProxySQL 读写分离
    《抛弃learning rate decay吧!》
    《Tensorflow 中 learning rate decay 的奇技淫巧 》
  • 原文地址:https://www.cnblogs.com/joe235/p/11342534.html
Copyright © 2020-2023  润新知