[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 wechat-miniprogram\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# awesome-skyline\n\nSkyline 是微信小程序推出的新渲染引擎，用于替代 WebView 渲染，其能够带来更好的渲染性能，并且添加了诸多增强特性，让小程序拥有更接近原生的交互体验。更多细节可查看[文档](https://developers.weixin.qq.com/miniprogram/dev/framework/runtime/skyline/introduction.html)。\n\n## 示例集\n\n- [通讯录](./examples/address-book)\n- [相册](./examples/album)\n- [卡片转场](./examples/card_transition)\n- [半屏弹窗](./examples/half-screen)\n- [分段式半屏](./examples/segmented-half-screen)\n- [Tab 指示条](./examples/tab-indicator)\n- [搜索栏吸附](./examples/product-list)\n- [沉浸式商品浏览](./examples/expanded-scroll-view)\n- [分类列表联动](./examples/associated-scroll-view)\n"
  },
  {
    "path": "examples/address-book/.eslintrc.js",
    "content": "/*\n * Eslint config file\n * Documentation: https://eslint.org/docs/user-guide/configuring/\n * Install the Eslint extension before using this feature.\n */\nmodule.exports = {\n  env: {\n    es6: true,\n    browser: true,\n    node: true,\n  },\n  ecmaFeatures: {\n    modules: true,\n  },\n  parserOptions: {\n    ecmaVersion: 2018,\n    sourceType: 'module',\n  },\n  globals: {\n    wx: true,\n    App: true,\n    Page: true,\n    getCurrentPages: true,\n    getApp: true,\n    Component: true,\n    requirePlugin: true,\n    requireMiniProgram: true,\n  },\n  // extends: 'eslint:recommended',\n  rules: {},\n}\n"
  },
  {
    "path": "examples/address-book/app.js",
    "content": "// app.js\nApp({})\n"
  },
  {
    "path": "examples/address-book/app.json",
    "content": "{\n  \"pages\": [\n    \"pages/index/index\"\n  ],\n  \"window\": {\n    \"backgroundTextStyle\": \"light\",\n    \"navigationBarBackgroundColor\": \"#fff\",\n    \"navigationBarTitleText\": \"Weixin\",\n    \"navigationBarTextStyle\": \"black\"\n  },\n  \"style\": \"v2\",\n  \"renderer\": \"skyline\",\n  \"rendererOptions\": {\n    \"skyline\": {\n      \"defaultDisplayBlock\": true,\n      \"disableABTest\": true,\n      \"sdkVersionBegin\": \"3.0.0\",\n      \"sdkVersionEnd\": \"15.255.255\"\n    }\n  },\n  \"lazyCodeLoading\": \"requiredComponents\",\n  \"componentFramework\": \"glass-easel\",\n  \"sitemapLocation\": \"sitemap.json\"\n}"
  },
  {
    "path": "examples/address-book/app.wxss",
    "content": ""
  },
  {
    "path": "examples/address-book/components/address-book/index.js",
    "content": "const throttle = function throttle(func, wait, options) {\n  let context\n  let args\n  let result = void 0\n  let timeout\n  let previous = 0\n  if (!options) options = {}\n  const later = function later() {\n    previous = options.leading === false ? 0 : Date.now()\n    timeout = null\n    result = func.apply(context, args)\n    if (!timeout) context = args = null\n  }\n  return function () {\n    const now = Date.now()\n    if (!previous && options.leading === false) previous = now\n    const remaining = wait - (now - previous)\n    context = this\n    args = arguments\n    if (remaining <= 0 || remaining > wait) {\n      clearTimeout(timeout)\n      timeout = null\n      previous = now\n      result = func.apply(context, args)\n      if (!timeout) context = args = null\n    } else if (!timeout && options.trailing !== false) {\n      timeout = setTimeout(later, remaining)\n    }\n    return result\n  }\n}\n\nComponent({\n  behaviors: [],\n  options: {\n    addGlobalClass: false,\n    virtualHost: true,\n    pureDataPattern: /^_/,\n  },\n  properties: {\n    list: {\n      type: Array,\n      value: [],\n      observer(newVal) {\n        if (newVal.length === 0) return\n        const alphabet = this.data.list.map((item, index) => {\n          this.data._tops[index] = 2e10\n          return item.alpha\n        })\n        this._sharedTops.value = this.data._tops\n        this.setData({\n          alphabet,\n          current: alphabet[0]\n        }, () => {\n          this.computedSize()\n        })\n      },\n    },\n  },\n\n  data: {\n    current: 'A',\n    intoView: '',\n    touching: false,\n    alphabet: [],\n    _vibrated: true,\n    _tops: [],\n    _anchorItemH: 0,\n    _anchorItemW: 0,\n    _anchorTop: 0,\n  },\n\n  observers: {\n    'current': function (current) {\n      this._sharedCurrentIdx.value = this.data.alphabet.indexOf(current)\n    },\n  },\n\n  lifetimes: {\n    created() {\n      this._handlePan = throttle(this._handlePan, 100, {})\n      this._sharedTops = wx.worklet.shared([])\n      this._sharedScrollTop = wx.worklet.shared(0)\n      this._sharedHeight = wx.worklet.shared(0)\n      this._sharedCurrentIdx = wx.worklet.shared(0)\n    },\n    attached() {\n      // scroll-view 高度\n      this.createSelectorQuery().select('.scroll-view').boundingClientRect(res => {\n        this._sharedHeight.value = res.height\n      }).exec()\n\n      // 右侧目录计算\n      const query = this.createSelectorQuery()\n      query.select('.anchor-list').boundingClientRect(rect => {\n        this.data._anchorItemH = rect.height / this.data.alphabet.length\n        this.data._anchorItemW = rect.width\n        this.data._anchorTop = rect.top\n      }).exec()\n    },\n  },\n  methods: {\n    handlePan(e) {\n      this._handlePan(e)\n    },\n    _handlePan(e) {\n      const data = this.data\n      const clientY = e.changedTouches[0].clientY\n      const index = Math.floor((clientY - data._anchorTop) / data._anchorItemH)\n      const current = data.alphabet[index]\n      if (current !== this.data.current) {\n        wx.vibrateShort({\n          type: 'light'\n        })\n        this.setData({\n          current,\n          intoView: current,\n          touching: true\n        })\n      }\n    },\n    cancelPan() {\n      setTimeout(() => {\n        this.setData({ touching: false })\n      }, 150)\n    },\n    computedSize() {\n      this.data.alphabet.forEach((element, index) => {\n        // NOTE: 在 Skyline 下如果用了 list-view / grid-view 会有按需渲染特性，取其子节点的 clientRect\n        // 时若不在屏会取不到，而这里是取 sticky-header 的，会立即渲染也就能立即返回，但 top 值是预估的。\n        // 因为 list-view 的高度是预估的（第一个子节点的高度 * 数量），由于其子节点是等高，故预估是基本准确的\n        this.createSelectorQuery().select(`#${element}`).boundingClientRect(res => {\n          this.data._tops[index] = res.top\n          this._sharedTops.value = this.data._tops\n        }).exec()\n      })\n    },\n    handleScroll(e) {\n      'worklet'\n      const scrollTop = e.detail.scrollTop\n      // 用于计算每个 header 的 offsetTop\n      this._sharedScrollTop.value = scrollTop\n\n      // 下面判断当前选中态，按需更新\n      const tops = this._sharedTops.value\n      for (let i = tops.length - 1; i >= 0; i--) {\n        // header 超过屏幕一半就改为选中态\n        if (scrollTop + this._sharedHeight.value / 2 > tops[i]) {\n          if (i !== this._sharedCurrentIdx.value) {\n            // worklet 函数运行在 UI 线程，setData 调用要抛回 JS 线程执行\n            wx.worklet.runOnJS(this.updateCurrent.bind(this))(i)\n          }\n          break\n        }\n      }\n    },\n    updateCurrent(idx) {\n      if (this.data.touching) return\n      this.setData({ current: this.data.alphabet[idx] })\n    },\n  }\n})\n"
  },
  {
    "path": "examples/address-book/components/address-book/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {},\n  \"addGlobalClass\": false,\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/address-book/components/address-book/index.wxml",
    "content": "<!-- 主列表 -->\n<scroll-view\n  type=\"custom\"\n  scroll-y\n  show-scrollbar=\"{{false}}\"\n  scroll-into-view=\"{{intoView}}\"\n  class=\"scroll-view\"\n  bind:scroll=\"handleScroll\"\n>\n  <view><slot></slot></view>\n  <sticky-section wx:for=\"{{list}}\" wx:key=\"alpha\">\n    <sticky-header>\n      <view class=\"city-group__title tips-color\" id=\"{{item.alpha}}\">{{item.alpha}}</view>\n    </sticky-header>\n    <list-view>\n      <block wx:for=\"{{item.subItems}}\" wx:for-item=\"subItem\" wx:key=\"name\">\n        <view class=\"city-group__item thin-border-bottom\" hover-class=\"bg-highlight\">{{subItem.name}}</view>\n      </block>\n    </list-view>\n  </sticky-section>\n</scroll-view>\n<!-- 右侧目录 -->\n<root-portal>\n  <!-- 这里也可以用 pan-gesture-handler -->\n  <view class=\"wx-flex right-directory\" catch:touchstart='handlePan' catch:touchmove='handlePan' catch:touchend='cancelPan'>\n    <view class=\"anchor-bar\">\n      <view class=\"anchor-list\">\n        <block wx:for=\"{{alphabet}}\" wx:key=\"*this\" wx:for-item=\"alpha\">\n          <view data-alpha=\"{{alpha}}\">\n            <view class=\"anchor-item {{current == alpha ? ( touching ? 'selected tapped' : 'selected' ): ''}}\">\n              <view class=\"anchor-item__inner\">{{alpha}}</view>\n              <view class=\"anchor-item__pop\">\n                {{alpha}}\n                <view class=\"anchor-item__pop_after\"></view>\n              </view>\n            </view>\n          </view>\n        </block>\n      </view>\n    </view>\n  </view>\n</root-portal>\n"
  },
  {
    "path": "examples/address-book/components/address-book/index.wxss",
    "content": ".wx-flex {\n  display: flex;\n  align-items: center\n}\n\n.scroll-view {\n  height: 100%;\n}\n\n.thin-border-bottom {\n  position: relative\n}\n\n.thin-border-top {\n  position: relative\n}\n\n.square-tag {\n  color: #9a9a9a;\n  text-align: center;\n  height: 30px;\n  line-height: 30px;\n  box-sizing: border-box;\n  border-radius: 2px;\n  background-color: #f7f7f7;\n  font-size: 12px;\n  position: relative\n}\n\n.square-tag.selected {\n  background: rgba(26, 173, 25, 0.1);\n  color: #1AAD19\n}\n\n.select-city__hd {\n  padding: 0 15px;\n  position: fixed;\n  height: 50px;\n  background-color: #fff;\n  left: 0;\n  right: 0;\n  z-index: 990\n}\n\n.current-city__name {\n  display: inline-block;\n  margin-right: 10px;\n  margin-left: 11px\n}\n\n.city-group_part .city-group__title {\n  padding-bottom: 12px\n}\n\n.city-group__title {\n  padding: 12px 12px 11px\n}\n\n.square-tag {\n  width: 100px;\n  display: inline-block;\n  margin-right: 12px;\n  margin-bottom: 12px;\n  height: 40px;\n  line-height: 40px;\n  font-size: 15px;\n  color: #000;\n  background-color: rgba(0, 0, 0, 0.02);\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis\n}\n\n.city-group__item {\n  padding: 15px 12px;\n  font-size: 15px\n}\n\n.city-group_all {\n  padding-bottom: 50px\n}\n\n.fixed__top {\n  position: fixed;\n  top: 0\n}\n\n.anchor-bar__wrp {\n  position: fixed;\n  top: 0;\n  bottom: 0;\n  right: 0;\n  width: 30px;\n  z-index: 999\n}\n\n.anchor-item {\n  font-size: 0;\n  text-align: center;\n  position: relative\n}\n\n.anchor-item__inner {\n  line-height: 10px;\n  height: 14px;\n  width: 14px;\n  border-radius: 50%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 10px;\n  margin: 1px 0;\n  font-weight: 500\n}\n\n.tapped .anchor-item__pop {\n  display: flex;\n}\n\n.anchor-item__pop {\n  position: absolute;\n  font-size: 50px;\n  width: 55px;\n  height: 55px;\n  line-height: 45px;\n  color: #fff;\n  background-color: #C9C9C9;\n  border-radius: 50%;\n  border: 5px solid transparent;\n  right: 40px;\n  top: 50%;\n  transform: translateY(-50%);\n  display: none;\n  box-sizing: border-box;\n  align-items: center;\n  justify-content: center;\n}\n\n.anchor-item__pop_after {\n  position: absolute;\n  width: 0;\n  height: 0;\n  left: 42px;\n  border: 20px solid;\n  border-color: transparent transparent transparent #C9C9C9;\n  top: 50%;\n  transform: translateY(-50%)\n}\n\n.anchor-item.selected .anchor-item__inner {\n  color: #fff;\n  background-color: #1aad19\n}\n\n.right-directory {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 1;\n  width: 30px;\n  height: 100vh;\n  justify-content: center;\n  flex-direction: column;\n}\n.anchor-bar {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  width: 30px;\n}\n\n.tips-color {\n  color: #5d5d5d;\n  background-color: #EAEAEA;\n  font-weight: bold;\n}\n\nsticky-header {\n  position: sticky;\n  top: 0;\n  z-index: 1;\n  display: block;\n}\n"
  },
  {
    "path": "examples/address-book/components/navigation-bar/index.js",
    "content": "Component({\n  options: {\n    multipleSlots: true // 在组件定义时的选项中启用多slot支持\n  },\n  /**\n   * 组件的属性列表\n   */\n  properties: {\n    extClass: {\n      type: String,\n      value: ''\n    },\n    title: {\n      type: String,\n      value: ''\n    },\n    background: {\n      type: String,\n      value: ''\n    },\n    color: {\n      type: String,\n      value: ''\n    },\n    back: {\n      type: Boolean,\n      value: true\n    },\n    loading: {\n      type: Boolean,\n      value: false\n    },\n    animated: {\n      // 显示隐藏的时候opacity动画效果\n      type: Boolean,\n      value: true\n    },\n    show: {\n      // 显示隐藏导航，隐藏的时候navigation-bar的高度占位还在\n      type: Boolean,\n      value: true,\n      observer: '_showChange'\n    },\n    // back为true的时候，返回的页面深度\n    delta: {\n      type: Number,\n      value: 1\n    }\n  },\n  /**\n   * 组件的初始数据\n   */\n  data: {\n    displayStyle: ''\n  },\n  attached() {\n    const rect = wx.getMenuButtonBoundingClientRect()\n    wx.getSystemInfo({\n      success: (res) => {\n        this.setData({\n          statusBarHeight: res.statusBarHeight,\n          innerPaddingRight: `padding-right:${res.windowWidth - rect.left}px`,\n          leftWidth: `width:${res.windowWidth - rect.left}px`,\n          navBarHeight: rect.bottom + rect.top - res.statusBarHeight,\n        })\n      }\n    })\n  },\n  /**\n   * 组件的方法列表\n   */\n  methods: {\n    _showChange(show) {\n      const animated = this.data.animated\n      let displayStyle = ''\n      if (animated) {\n        displayStyle = `opacity: ${show ? '1' : '0'};transition: opacity 0.5s;`\n      } else {\n        displayStyle = `display: ${show ? '' : 'none'}`\n      }\n      this.setData({\n        displayStyle\n      })\n    },\n    back() {\n      const data = this.data\n      if (data.delta) {\n        wx.navigateBack({\n          delta: data.delta\n        })\n      }\n      this.triggerEvent('back', { delta: data.delta }, {})\n    }\n  }\n})\n"
  },
  {
    "path": "examples/address-book/components/navigation-bar/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {},\n  \"addGlobalClass\": true,\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/address-book/components/navigation-bar/index.wxml",
    "content": "<view class=\"weui-navigation-bar {{extClass}}\">\n  <view class=\"weui-navigation-bar__inner\" style=\"padding-top: {{statusBarHeight}}px; height: {{navBarHeight}}px; color: {{color}}; background: {{background}}; {{displayStyle}}; {{innerPaddingRight}};\">\n    <view class='weui-navigation-bar__left' style=\"{{leftWidth}}\">\n      <block wx:if=\"{{back}}\">\n        <view class=\"navigation-bar__buttons\" bindtap=\"back\">\n          <view class=\"navigation-bar__button navigation-bar__btn_goback\" hover-class=\"active\"></view>\n        </view>\n      </block>\n      <block wx:else>\n        <slot name=\"left\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__center'>\n      <view wx:if=\"{{loading}}\" class=\"weui-navigation-bar__loading\" aria-role=\"alert\">\n        <view class=\"weui-loading\" style=\"width:{{size.width}}rpx;height:{{size.height}}rpx;\" aria-role=\"img\" aria-label=\"加载中\"></view>\n      </view>\n      <block wx:if=\"{{title}}\">\n        <text>{{title}}</text>\n      </block>\n      <block wx:else>\n        <slot name=\"center\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__right'>\n      <slot name=\"right\"></slot>\n    </view>\n  </view>\n</view>\n"
  },
  {
    "path": "examples/address-book/components/navigation-bar/index.wxss",
    "content": ".weui-navigation-bar {\n  overflow: hidden;\n  color: rgba(0, 0, 0, .9);\n  width: 100vw;\n}\n\n.weui-navigation-bar__placeholder {\n  background: #f7f7f7;\n  position: relative;\n}\n\n.weui-navigation-bar__inner, .weui-navigation-bar__inner .weui-navigation-bar__left {\n  display: flex;\n  align-items: center;\n  flex-direction: row;\n}\n\n.weui-navigation-bar__inner {\n  position: relative;\n  padding-right: 95px;\n  width: 100vw;\n  box-sizing: border-box;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left {\n  position: relative;\n  width: 95px;\n  padding-left: 16px;\n  box-sizing: border-box;\n}\n\n.weui-navigation-bar__btn_goback_wrapper {\n  padding: 11px 18px 11px 16px;\n  margin: -11px -18px -11px -16px;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left .navigation-bar__btn_goback {\n  font-size: 12px;\n  width: 12px;\n  height: 24px;\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E\") no-repeat 50% 50%;\n  background-size: cover;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__center {\n  font-size: 17px;\n  text-align: center;\n  position: relative;\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-weight: bold;\n}\n\n@media(prefers-color-scheme: dark) {\n  .weui-navigation-bar {\n    color: hsla(0, 0%, 100%, .8);\n  }\n  .weui-navigation-bar__inner {\n    background-color: #1f1f1f;\n  }\n}\n"
  },
  {
    "path": "examples/address-book/pages/index/data.js",
    "content": "export const cityData = [{ \"fullname\": \"东城区\", \"id\": \"110101\", \"location\": { \"lat\": 39.92855, \"lng\": 116.41637 }, \"name\": \"东城\", \"pinyin\": [\"dong\", \"cheng\"] }, { \"fullname\": \"西城区\", \"id\": \"110102\", \"location\": { \"lat\": 39.91231, \"lng\": 116.36611 }, \"name\": \"西城\", \"pinyin\": [\"xi\", \"cheng\"] }, { \"fullname\": \"朝阳区\", \"id\": \"110105\", \"location\": { \"lat\": 39.9219, \"lng\": 116.44355 }, \"name\": \"朝阳\", \"pinyin\": [\"chao\", \"yang\"] }, { \"fullname\": \"丰台区\", \"id\": \"110106\", \"location\": { \"lat\": 39.85856, \"lng\": 116.28616 }, \"name\": \"丰台\", \"pinyin\": [\"feng\", \"tai\"] }, { \"fullname\": \"石景山区\", \"id\": \"110107\", \"location\": { \"lat\": 39.90569, \"lng\": 116.22299 }, \"name\": \"石景山\", \"pinyin\": [\"shi\", \"jing\", \"shan\"] }, { \"fullname\": \"海淀区\", \"id\": \"110108\", \"location\": { \"lat\": 39.95933, \"lng\": 116.29845 }, \"name\": \"海淀\", \"pinyin\": [\"hai\", \"dian\"] }, { \"fullname\": \"门头沟区\", \"id\": \"110109\", \"location\": { \"lat\": 39.94048, \"lng\": 116.10146 }, \"name\": \"门头沟\", \"pinyin\": [\"men\", \"tou\", \"gou\"] }, { \"fullname\": \"房山区\", \"id\": \"110111\", \"location\": { \"lat\": 39.74788, \"lng\": 116.14294 }, \"name\": \"房山\", \"pinyin\": [\"fang\", \"shan\"] }, { \"fullname\": \"通州区\", \"id\": \"110112\", \"location\": { \"lat\": 39.90998, \"lng\": 116.65714 }, \"name\": \"通州\", \"pinyin\": [\"tong\", \"zhou\"] }, { \"fullname\": \"顺义区\", \"id\": \"110113\", \"location\": { \"lat\": 40.13012, \"lng\": 116.65477 }, \"name\": \"顺义\", \"pinyin\": [\"shun\", \"yi\"] }, { \"fullname\": \"昌平区\", \"id\": \"110114\", \"location\": { \"lat\": 40.22077, \"lng\": 116.23128 }, \"name\": \"昌平\", \"pinyin\": [\"chang\", \"ping\"] }, { \"fullname\": \"大兴区\", \"id\": \"110115\", \"location\": { \"lat\": 39.72684, \"lng\": 116.34159 }, \"name\": \"大兴\", \"pinyin\": [\"da\", \"xing\"] }, { \"fullname\": \"怀柔区\", \"id\": \"110116\", \"location\": { \"lat\": 40.316, \"lng\": 116.63177 }, \"name\": \"怀柔\", \"pinyin\": [\"huai\", \"rou\"] }, { \"fullname\": \"平谷区\", \"id\": \"110117\", \"location\": { \"lat\": 40.14062, \"lng\": 117.12141 }, \"name\": \"平谷\", \"pinyin\": [\"ping\", \"gu\"] }, { \"fullname\": \"密云区\", \"id\": \"110118\", \"location\": { \"lat\": 40.37625, \"lng\": 116.84317 }, \"name\": \"密云\", \"pinyin\": [\"mi\", \"yun\"] }, { \"fullname\": \"延庆区\", \"id\": \"110119\", \"location\": { \"lat\": 40.45678, \"lng\": 115.97503 }, \"name\": \"延庆\", \"pinyin\": [\"yan\", \"qing\"] }, { \"fullname\": \"和平区\", \"id\": \"120101\", \"location\": { \"lat\": 39.11712, \"lng\": 117.2147 }, \"name\": \"和平\", \"pinyin\": [\"he\", \"ping\"] }, { \"fullname\": \"河东区\", \"id\": \"120102\", \"location\": { \"lat\": 39.12827, \"lng\": 117.25228 }, \"name\": \"河东\", \"pinyin\": [\"he\", \"dong\"] }, { \"fullname\": \"河西区\", \"id\": \"120103\", \"location\": { \"lat\": 39.10954, \"lng\": 117.22336 }, \"name\": \"河西\", \"pinyin\": [\"he\", \"xi\"] }, { \"fullname\": \"南开区\", \"id\": \"120104\", \"location\": { \"lat\": 39.13815, \"lng\": 117.15011 }, \"name\": \"南开\", \"pinyin\": [\"nan\", \"kai\"] }, { \"fullname\": \"河北区\", \"id\": \"120105\", \"location\": { \"lat\": 39.14784, \"lng\": 117.19674 }, \"name\": \"河北\", \"pinyin\": [\"he\", \"bei\"] }, { \"fullname\": \"红桥区\", \"id\": \"120106\", \"location\": { \"lat\": 39.16734, \"lng\": 117.15161 }, \"name\": \"红桥\", \"pinyin\": [\"hong\", \"qiao\"] }, { \"fullname\": \"东丽区\", \"id\": \"120110\", \"location\": { \"lat\": 39.08652, \"lng\": 117.31428 }, \"name\": \"东丽\", \"pinyin\": [\"dong\", \"li\"] }, { \"fullname\": \"西青区\", \"id\": \"120111\", \"location\": { \"lat\": 39.14111, \"lng\": 117.00739 }, \"name\": \"西青\", \"pinyin\": [\"xi\", \"qing\"] }, { \"fullname\": \"津南区\", \"id\": \"120112\", \"location\": { \"lat\": 38.9375, \"lng\": 117.3571 }, \"name\": \"津南\", \"pinyin\": [\"jin\", \"nan\"] }, { \"fullname\": \"北辰区\", \"id\": \"120113\", \"location\": { \"lat\": 39.22393, \"lng\": 117.13544 }, \"name\": \"北辰\", \"pinyin\": [\"bei\", \"chen\"] }, { \"fullname\": \"武清区\", \"id\": \"120114\", \"location\": { \"lat\": 39.38408, \"lng\": 117.0443 }, \"name\": \"武清\", \"pinyin\": [\"wu\", \"qing\"] }, { \"fullname\": \"宝坻区\", \"id\": \"120115\", \"location\": { \"lat\": 39.71755, \"lng\": 117.30983 }, \"name\": \"宝坻\", \"pinyin\": [\"bao\", \"di\"] }, { \"fullname\": \"滨海新区\", \"id\": \"120116\", \"location\": { \"lat\": 39.0032, \"lng\": 117.71071 }, \"name\": \"滨海\", \"pinyin\": [\"bin\", \"hai\"] }, { \"fullname\": \"宁河区\", \"id\": \"120117\", \"location\": { \"lat\": 39.33091, \"lng\": 117.82478 }, \"name\": \"宁河\", \"pinyin\": [\"ning\", \"he\"] }, { \"fullname\": \"静海区\", \"id\": \"120118\", \"location\": { \"lat\": 38.94737, \"lng\": 116.97428 }, \"name\": \"静海\", \"pinyin\": [\"jing\", \"hai\"] }, { \"fullname\": \"蓟州区\", \"id\": \"120119\", \"location\": { \"lat\": 40.04577, \"lng\": 117.40829 }, \"name\": \"蓟州\", \"pinyin\": [\"ji\", \"zhou\"] }, { \"cidx\": [0, 21], \"fullname\": \"石家庄市\", \"id\": \"130100\", \"location\": { \"lat\": 38.04276, \"lng\": 114.5143 }, \"name\": \"石家庄\", \"pinyin\": [\"shi\", \"jia\", \"zhuang\"] }, { \"cidx\": [22, 36], \"fullname\": \"唐山市\", \"id\": \"130200\", \"location\": { \"lat\": 39.63048, \"lng\": 118.18058 }, \"name\": \"唐山\", \"pinyin\": [\"tang\", \"shan\"] }, { \"cidx\": [37, 43], \"fullname\": \"秦皇岛市\", \"id\": \"130300\", \"location\": { \"lat\": 39.93545, \"lng\": 119.59964 }, \"name\": \"秦皇岛\", \"pinyin\": [\"qin\", \"huang\", \"dao\"] }, { \"cidx\": [44, 61], \"fullname\": \"邯郸市\", \"id\": \"130400\", \"location\": { \"lat\": 36.62556, \"lng\": 114.53918 }, \"name\": \"邯郸\", \"pinyin\": [\"han\", \"dan\"] }, { \"cidx\": [62, 80], \"fullname\": \"邢台市\", \"id\": \"130500\", \"location\": { \"lat\": 37.07055, \"lng\": 114.50443 }, \"name\": \"邢台\", \"pinyin\": [\"xing\", \"tai\"] }, { \"cidx\": [81, 104], \"fullname\": \"保定市\", \"id\": \"130600\", \"location\": { \"lat\": 38.87396, \"lng\": 115.46459 }, \"name\": \"保定\", \"pinyin\": [\"bao\", \"ding\"] }, { \"cidx\": [105, 120], \"fullname\": \"张家口市\", \"id\": \"130700\", \"location\": { \"lat\": 40.82444, \"lng\": 114.88755 }, \"name\": \"张家口\", \"pinyin\": [\"zhang\", \"jia\", \"kou\"] }, { \"cidx\": [121, 131], \"fullname\": \"承德市\", \"id\": \"130800\", \"location\": { \"lat\": 40.9515, \"lng\": 117.9634 }, \"name\": \"承德\", \"pinyin\": [\"cheng\", \"de\"] }, { \"cidx\": [132, 147], \"fullname\": \"沧州市\", \"id\": \"130900\", \"location\": { \"lat\": 38.30441, \"lng\": 116.83869 }, \"name\": \"沧州\", \"pinyin\": [\"cang\", \"zhou\"] }, { \"cidx\": [148, 157], \"fullname\": \"廊坊市\", \"id\": \"131000\", \"location\": { \"lat\": 39.53775, \"lng\": 116.68376 }, \"name\": \"廊坊\", \"pinyin\": [\"lang\", \"fang\"] }, { \"cidx\": [158, 168], \"fullname\": \"衡水市\", \"id\": \"131100\", \"location\": { \"lat\": 37.73886, \"lng\": 115.67054 }, \"name\": \"衡水\", \"pinyin\": [\"heng\", \"shui\"] }, { \"cidx\": [169, 178], \"fullname\": \"太原市\", \"id\": \"140100\", \"location\": { \"lat\": 37.87059, \"lng\": 112.55067 }, \"name\": \"太原\", \"pinyin\": [\"tai\", \"yuan\"] }, { \"cidx\": [179, 188], \"fullname\": \"大同市\", \"id\": \"140200\", \"location\": { \"lat\": 40.07637, \"lng\": 113.30001 }, \"name\": \"大同\", \"pinyin\": [\"da\", \"tong\"] }, { \"cidx\": [189, 193], \"fullname\": \"阳泉市\", \"id\": \"140300\", \"location\": { \"lat\": 37.85668, \"lng\": 113.58047 }, \"name\": \"阳泉\", \"pinyin\": [\"yang\", \"quan\"] }, { \"cidx\": [194, 205], \"fullname\": \"长治市\", \"id\": \"140400\", \"location\": { \"lat\": 36.19581, \"lng\": 113.11649 }, \"name\": \"长治\", \"pinyin\": [\"chang\", \"zhi\"] }, { \"cidx\": [206, 211], \"fullname\": \"晋城市\", \"id\": \"140500\", \"location\": { \"lat\": 35.49039, \"lng\": 112.85113 }, \"name\": \"晋城\", \"pinyin\": [\"jin\", \"cheng\"] }, { \"cidx\": [212, 217], \"fullname\": \"朔州市\", \"id\": \"140600\", \"location\": { \"lat\": 39.33155, \"lng\": 112.43286 }, \"name\": \"朔州\", \"pinyin\": [\"shuo\", \"zhou\"] }, { \"cidx\": [218, 228], \"fullname\": \"晋中市\", \"id\": \"140700\", \"location\": { \"lat\": 37.68702, \"lng\": 112.75278 }, \"name\": \"晋中\", \"pinyin\": [\"jin\", \"zhong\"] }, { \"cidx\": [229, 241], \"fullname\": \"运城市\", \"id\": \"140800\", \"location\": { \"lat\": 35.02628, \"lng\": 111.00699 }, \"name\": \"运城\", \"pinyin\": [\"yun\", \"cheng\"] }, { \"cidx\": [242, 255], \"fullname\": \"忻州市\", \"id\": \"140900\", \"location\": { \"lat\": 38.4167, \"lng\": 112.73418 }, \"name\": \"忻州\", \"pinyin\": [\"xin\", \"zhou\"] }, { \"cidx\": [256, 272], \"fullname\": \"临汾市\", \"id\": \"141000\", \"location\": { \"lat\": 36.08822, \"lng\": 111.51962 }, \"name\": \"临汾\", \"pinyin\": [\"lin\", \"fen\"] }, { \"cidx\": [273, 285], \"fullname\": \"吕梁市\", \"id\": \"141100\", \"location\": { \"lat\": 37.51934, \"lng\": 111.14165 }, \"name\": \"吕梁\", \"pinyin\": [\"lv\", \"liang\"] }, { \"cidx\": [286, 294], \"fullname\": \"呼和浩特市\", \"id\": \"150100\", \"location\": { \"lat\": 40.84149, \"lng\": 111.75199 }, \"name\": \"呼和浩特\", \"pinyin\": [\"hu\", \"he\", \"hao\", \"te\"] }, { \"cidx\": [295, 303], \"fullname\": \"包头市\", \"id\": \"150200\", \"location\": { \"lat\": 40.65781, \"lng\": 109.84021 }, \"name\": \"包头\", \"pinyin\": [\"bao\", \"tou\"] }, { \"cidx\": [304, 306], \"fullname\": \"乌海市\", \"id\": \"150300\", \"location\": { \"lat\": 39.65384, \"lng\": 106.79546 }, \"name\": \"乌海\", \"pinyin\": [\"wu\", \"hai\"] }, { \"cidx\": [307, 318], \"fullname\": \"赤峰市\", \"id\": \"150400\", \"location\": { \"lat\": 42.2586, \"lng\": 118.88894 }, \"name\": \"赤峰\", \"pinyin\": [\"chi\", \"feng\"] }, { \"cidx\": [319, 326], \"fullname\": \"通辽市\", \"id\": \"150500\", \"location\": { \"lat\": 43.65247, \"lng\": 122.24469 }, \"name\": \"通辽\", \"pinyin\": [\"tong\", \"liao\"] }, { \"cidx\": [327, 335], \"fullname\": \"鄂尔多斯市\", \"id\": \"150600\", \"location\": { \"lat\": 39.60845, \"lng\": 109.78087 }, \"name\": \"鄂尔多斯\", \"pinyin\": [\"e\", \"er\", \"duo\", \"si\"] }, { \"cidx\": [336, 349], \"fullname\": \"呼伦贝尔市\", \"id\": \"150700\", \"location\": { \"lat\": 49.21163, \"lng\": 119.76584 }, \"name\": \"呼伦贝尔\", \"pinyin\": [\"hu\", \"lun\", \"bei\", \"er\"] }, { \"cidx\": [350, 356], \"fullname\": \"巴彦淖尔市\", \"id\": \"150800\", \"location\": { \"lat\": 40.74317, \"lng\": 107.38773 }, \"name\": \"巴彦淖尔\", \"pinyin\": [\"ba\", \"yan\", \"nao\", \"er\"] }, { \"cidx\": [357, 367], \"fullname\": \"乌兰察布市\", \"id\": \"150900\", \"location\": { \"lat\": 40.99391, \"lng\": 113.13376 }, \"name\": \"乌兰察布\", \"pinyin\": [\"wu\", \"lan\", \"cha\", \"bu\"] }, { \"cidx\": [368, 373], \"fullname\": \"兴安盟\", \"id\": \"152200\", \"location\": { \"lat\": 46.08208, \"lng\": 122.03818 }, \"name\": \"兴安\", \"pinyin\": [\"xing\", \"an\"] }, { \"cidx\": [374, 385], \"fullname\": \"锡林郭勒盟\", \"id\": \"152500\", \"location\": { \"lat\": 43.9332, \"lng\": 116.04775 }, \"name\": \"锡林郭勒\", \"pinyin\": [\"xi\", \"lin\", \"guo\", \"le\"] }, { \"cidx\": [386, 388], \"fullname\": \"阿拉善盟\", \"id\": \"152900\", \"location\": { \"lat\": 38.85153, \"lng\": 105.72898 }, \"name\": \"阿拉善\", \"pinyin\": [\"a\", \"la\", \"shan\"] }, { \"cidx\": [389, 401], \"fullname\": \"沈阳市\", \"id\": \"210100\", \"location\": { \"lat\": 41.67718, \"lng\": 123.4631 }, \"name\": \"沈阳\", \"pinyin\": [\"shen\", \"yang\"] }, { \"cidx\": [402, 411], \"fullname\": \"大连市\", \"id\": \"210200\", \"location\": { \"lat\": 38.91369, \"lng\": 121.61476 }, \"name\": \"大连\", \"pinyin\": [\"da\", \"lian\"] }, { \"cidx\": [412, 418], \"fullname\": \"鞍山市\", \"id\": \"210300\", \"location\": { \"lat\": 41.10777, \"lng\": 122.9946 }, \"name\": \"鞍山\", \"pinyin\": [\"an\", \"shan\"] }, { \"cidx\": [419, 425], \"fullname\": \"抚顺市\", \"id\": \"210400\", \"location\": { \"lat\": 41.87971, \"lng\": 123.95722 }, \"name\": \"抚顺\", \"pinyin\": [\"fu\", \"shun\"] }, { \"cidx\": [426, 431], \"fullname\": \"本溪市\", \"id\": \"210500\", \"location\": { \"lat\": 41.29413, \"lng\": 123.76686 }, \"name\": \"本溪\", \"pinyin\": [\"ben\", \"xi\"] }, { \"cidx\": [432, 437], \"fullname\": \"丹东市\", \"id\": \"210600\", \"location\": { \"lat\": 39.9998, \"lng\": 124.35601 }, \"name\": \"丹东\", \"pinyin\": [\"dan\", \"dong\"] }, { \"cidx\": [438, 444], \"fullname\": \"锦州市\", \"id\": \"210700\", \"location\": { \"lat\": 41.09515, \"lng\": 121.12703 }, \"name\": \"锦州\", \"pinyin\": [\"jin\", \"zhou\"] }, { \"cidx\": [445, 450], \"fullname\": \"营口市\", \"id\": \"210800\", \"location\": { \"lat\": 40.66683, \"lng\": 122.2349 }, \"name\": \"营口\", \"pinyin\": [\"ying\", \"kou\"] }, { \"cidx\": [451, 457], \"fullname\": \"阜新市\", \"id\": \"210900\", \"location\": { \"lat\": 42.02166, \"lng\": 121.67011 }, \"name\": \"阜新\", \"pinyin\": [\"fu\", \"xin\"] }, { \"cidx\": [458, 464], \"fullname\": \"辽阳市\", \"id\": \"211000\", \"location\": { \"lat\": 41.26809, \"lng\": 123.23736 }, \"name\": \"辽阳\", \"pinyin\": [\"liao\", \"yang\"] }, { \"cidx\": [465, 468], \"fullname\": \"盘锦市\", \"id\": \"211100\", \"location\": { \"lat\": 41.11996, \"lng\": 122.07078 }, \"name\": \"盘锦\", \"pinyin\": [\"pan\", \"jin\"] }, { \"cidx\": [469, 475], \"fullname\": \"铁岭市\", \"id\": \"211200\", \"location\": { \"lat\": 42.2862, \"lng\": 123.84241 }, \"name\": \"铁岭\", \"pinyin\": [\"tie\", \"ling\"] }, { \"cidx\": [476, 482], \"fullname\": \"朝阳市\", \"id\": \"211300\", \"location\": { \"lat\": 41.57347, \"lng\": 120.4508 }, \"name\": \"朝阳\", \"pinyin\": [\"chao\", \"yang\"] }, { \"cidx\": [483, 488], \"fullname\": \"葫芦岛市\", \"id\": \"211400\", \"location\": { \"lat\": 40.711, \"lng\": 120.83699 }, \"name\": \"葫芦岛\", \"pinyin\": [\"hu\", \"lu\", \"dao\"] }, { \"cidx\": [489, 498], \"fullname\": \"长春市\", \"id\": \"220100\", \"location\": { \"lat\": 43.81602, \"lng\": 125.32357 }, \"name\": \"长春\", \"pinyin\": [\"chang\", \"chun\"] }, { \"cidx\": [499, 507], \"fullname\": \"吉林市\", \"id\": \"220200\", \"location\": { \"lat\": 43.83784, \"lng\": 126.54944 }, \"name\": \"吉林\", \"pinyin\": [\"ji\", \"lin\"] }, { \"cidx\": [508, 513], \"fullname\": \"四平市\", \"id\": \"220300\", \"location\": { \"lat\": 43.16646, \"lng\": 124.35036 }, \"name\": \"四平\", \"pinyin\": [\"si\", \"ping\"] }, { \"cidx\": [514, 517], \"fullname\": \"辽源市\", \"id\": \"220400\", \"location\": { \"lat\": 42.88805, \"lng\": 125.14368 }, \"name\": \"辽源\", \"pinyin\": [\"liao\", \"yuan\"] }, { \"cidx\": [518, 524], \"fullname\": \"通化市\", \"id\": \"220500\", \"location\": { \"lat\": 41.72829, \"lng\": 125.9399 }, \"name\": \"通化\", \"pinyin\": [\"tong\", \"hua\"] }, { \"cidx\": [525, 530], \"fullname\": \"白山市\", \"id\": \"220600\", \"location\": { \"lat\": 41.9408, \"lng\": 126.42443 }, \"name\": \"白山\", \"pinyin\": [\"bai\", \"shan\"] }, { \"cidx\": [531, 535], \"fullname\": \"松原市\", \"id\": \"220700\", \"location\": { \"lat\": 45.1411, \"lng\": 124.82515 }, \"name\": \"松原\", \"pinyin\": [\"song\", \"yuan\"] }, { \"cidx\": [536, 540], \"fullname\": \"白城市\", \"id\": \"220800\", \"location\": { \"lat\": 45.6196, \"lng\": 122.83871 }, \"name\": \"白城\", \"pinyin\": [\"bai\", \"cheng\"] }, { \"cidx\": [541, 548], \"fullname\": \"延边朝鲜族自治州\", \"id\": \"222400\", \"location\": { \"lat\": 42.89119, \"lng\": 129.5091 }, \"name\": \"延边\", \"pinyin\": [\"yan\", \"bian\"] }, { \"cidx\": [549, 566], \"fullname\": \"哈尔滨市\", \"id\": \"230100\", \"location\": { \"lat\": 45.80216, \"lng\": 126.5358 }, \"name\": \"哈尔滨\", \"pinyin\": [\"ha\", \"er\", \"bin\"] }, { \"cidx\": [567, 582], \"fullname\": \"齐齐哈尔市\", \"id\": \"230200\", \"location\": { \"lat\": 47.35431, \"lng\": 123.91796 }, \"name\": \"齐齐哈尔\", \"pinyin\": [\"qi\", \"qi\", \"ha\", \"er\"] }, { \"cidx\": [583, 591], \"fullname\": \"鸡西市\", \"id\": \"230300\", \"location\": { \"lat\": 45.29524, \"lng\": 130.96954 }, \"name\": \"鸡西\", \"pinyin\": [\"ji\", \"xi\"] }, { \"cidx\": [592, 599], \"fullname\": \"鹤岗市\", \"id\": \"230400\", \"location\": { \"lat\": 47.34989, \"lng\": 130.29785 }, \"name\": \"鹤岗\", \"pinyin\": [\"he\", \"gang\"] }, { \"cidx\": [600, 607], \"fullname\": \"双鸭山市\", \"id\": \"230500\", \"location\": { \"lat\": 46.64658, \"lng\": 131.1591 }, \"name\": \"双鸭山\", \"pinyin\": [\"shuang\", \"ya\", \"shan\"] }, { \"cidx\": [608, 616], \"fullname\": \"大庆市\", \"id\": \"230600\", \"location\": { \"lat\": 46.58758, \"lng\": 125.10307 }, \"name\": \"大庆\", \"pinyin\": [\"da\", \"qing\"] }, { \"cidx\": [617, 633], \"fullname\": \"伊春市\", \"id\": \"230700\", \"location\": { \"lat\": 47.72752, \"lng\": 128.84049 }, \"name\": \"伊春\", \"pinyin\": [\"yi\", \"chun\"] }, { \"cidx\": [634, 643], \"fullname\": \"佳木斯市\", \"id\": \"230800\", \"location\": { \"lat\": 46.79977, \"lng\": 130.31882 }, \"name\": \"佳木斯\", \"pinyin\": [\"jia\", \"mu\", \"si\"] }, { \"cidx\": [644, 647], \"fullname\": \"七台河市\", \"id\": \"230900\", \"location\": { \"lat\": 45.77065, \"lng\": 131.00306 }, \"name\": \"七台河\", \"pinyin\": [\"qi\", \"tai\", \"he\"] }, { \"cidx\": [648, 657], \"fullname\": \"牡丹江市\", \"id\": \"231000\", \"location\": { \"lat\": 44.55269, \"lng\": 129.63244 }, \"name\": \"牡丹江\", \"pinyin\": [\"mu\", \"dan\", \"jiang\"] }, { \"cidx\": [658, 663], \"fullname\": \"黑河市\", \"id\": \"231100\", \"location\": { \"lat\": 50.24523, \"lng\": 127.52852 }, \"name\": \"黑河\", \"pinyin\": [\"hei\", \"he\"] }, { \"cidx\": [664, 673], \"fullname\": \"绥化市\", \"id\": \"231200\", \"location\": { \"lat\": 46.65246, \"lng\": 126.96932 }, \"name\": \"绥化\", \"pinyin\": [\"sui\", \"hua\"] }, { \"cidx\": [674, 677], \"fullname\": \"大兴安岭地区\", \"id\": \"232700\", \"location\": { \"lat\": 51.92398, \"lng\": 124.59216 }, \"name\": \"大兴安岭\", \"pinyin\": [\"da\", \"xing\", \"an\", \"ling\"] }, { \"fullname\": \"黄浦区\", \"id\": \"310101\", \"location\": { \"lat\": 31.23162, \"lng\": 121.48461 }, \"name\": \"黄浦\", \"pinyin\": [\"huang\", \"pu\"] }, { \"fullname\": \"徐汇区\", \"id\": \"310104\", \"location\": { \"lat\": 31.18826, \"lng\": 121.43687 }, \"name\": \"徐汇\", \"pinyin\": [\"xu\", \"hui\"] }, { \"fullname\": \"长宁区\", \"id\": \"310105\", \"location\": { \"lat\": 31.22024, \"lng\": 121.42394 }, \"name\": \"长宁\", \"pinyin\": [\"chang\", \"ning\"] }, { \"fullname\": \"静安区\", \"id\": \"310106\", \"location\": { \"lat\": 31.22352, \"lng\": 121.45591 }, \"name\": \"静安\", \"pinyin\": [\"jing\", \"an\"] }, { \"fullname\": \"普陀区\", \"id\": \"310107\", \"location\": { \"lat\": 31.2494, \"lng\": 121.397 }, \"name\": \"普陀\", \"pinyin\": [\"pu\", \"tuo\"] }, { \"fullname\": \"虹口区\", \"id\": \"310109\", \"location\": { \"lat\": 31.26451, \"lng\": 121.50515 }, \"name\": \"虹口\", \"pinyin\": [\"hong\", \"kou\"] }, { \"fullname\": \"杨浦区\", \"id\": \"310110\", \"location\": { \"lat\": 31.25956, \"lng\": 121.52609 }, \"name\": \"杨浦\", \"pinyin\": [\"yang\", \"pu\"] }, { \"fullname\": \"闵行区\", \"id\": \"310112\", \"location\": { \"lat\": 31.11325, \"lng\": 121.38206 }, \"name\": \"闵行\", \"pinyin\": [\"min\", \"hang\"] }, { \"fullname\": \"宝山区\", \"id\": \"310113\", \"location\": { \"lat\": 31.40527, \"lng\": 121.48941 }, \"name\": \"宝山\", \"pinyin\": [\"bao\", \"shan\"] }, { \"fullname\": \"嘉定区\", \"id\": \"310114\", \"location\": { \"lat\": 31.37482, \"lng\": 121.26621 }, \"name\": \"嘉定\", \"pinyin\": [\"jia\", \"ding\"] }, { \"fullname\": \"浦东新区\", \"id\": \"310115\", \"location\": { \"lat\": 31.22114, \"lng\": 121.54409 }, \"name\": \"浦东\", \"pinyin\": [\"pu\", \"dong\"] }, { \"fullname\": \"金山区\", \"id\": \"310116\", \"location\": { \"lat\": 30.74185, \"lng\": 121.34242 }, \"name\": \"金山\", \"pinyin\": [\"jin\", \"shan\"] }, { \"fullname\": \"松江区\", \"id\": \"310117\", \"location\": { \"lat\": 31.03241, \"lng\": 121.22654 }, \"name\": \"松江\", \"pinyin\": [\"song\", \"jiang\"] }, { \"fullname\": \"青浦区\", \"id\": \"310118\", \"location\": { \"lat\": 31.14979, \"lng\": 121.12426 }, \"name\": \"青浦\", \"pinyin\": [\"qing\", \"pu\"] }, { \"fullname\": \"奉贤区\", \"id\": \"310120\", \"location\": { \"lat\": 30.91803, \"lng\": 121.4741 }, \"name\": \"奉贤\", \"pinyin\": [\"feng\", \"xian\"] }, { \"fullname\": \"崇明区\", \"id\": \"310151\", \"location\": { \"lat\": 31.6229, \"lng\": 121.3973 }, \"name\": \"崇明\", \"pinyin\": [\"chong\", \"ming\"] }, { \"cidx\": [678, 688], \"fullname\": \"南京市\", \"id\": \"320100\", \"location\": { \"lat\": 32.05838, \"lng\": 118.79647 }, \"name\": \"南京\", \"pinyin\": [\"nan\", \"jing\"] }, { \"cidx\": [689, 695], \"fullname\": \"无锡市\", \"id\": \"320200\", \"location\": { \"lat\": 31.49099, \"lng\": 120.31237 }, \"name\": \"无锡\", \"pinyin\": [\"wu\", \"xi\"] }, { \"cidx\": [696, 705], \"fullname\": \"徐州市\", \"id\": \"320300\", \"location\": { \"lat\": 34.2044, \"lng\": 117.28577 }, \"name\": \"徐州\", \"pinyin\": [\"xu\", \"zhou\"] }, { \"cidx\": [706, 711], \"fullname\": \"常州市\", \"id\": \"320400\", \"location\": { \"lat\": 31.81072, \"lng\": 119.97365 }, \"name\": \"常州\", \"pinyin\": [\"chang\", \"zhou\"] }, { \"cidx\": [712, 720], \"fullname\": \"苏州市\", \"id\": \"320500\", \"location\": { \"lat\": 31.29834, \"lng\": 120.58319 }, \"name\": \"苏州\", \"pinyin\": [\"su\", \"zhou\"] }, { \"cidx\": [721, 728], \"fullname\": \"南通市\", \"id\": \"320600\", \"location\": { \"lat\": 31.97958, \"lng\": 120.89371 }, \"name\": \"南通\", \"pinyin\": [\"nan\", \"tong\"] }, { \"cidx\": [729, 734], \"fullname\": \"连云港市\", \"id\": \"320700\", \"location\": { \"lat\": 34.59669, \"lng\": 119.22295 }, \"name\": \"连云港\", \"pinyin\": [\"lian\", \"yun\", \"gang\"] }, { \"cidx\": [735, 741], \"fullname\": \"淮安市\", \"id\": \"320800\", \"location\": { \"lat\": 33.61016, \"lng\": 119.01595 }, \"name\": \"淮安\", \"pinyin\": [\"huai\", \"an\"] }, { \"cidx\": [742, 750], \"fullname\": \"盐城市\", \"id\": \"320900\", \"location\": { \"lat\": 33.34951, \"lng\": 120.16164 }, \"name\": \"盐城\", \"pinyin\": [\"yan\", \"cheng\"] }, { \"cidx\": [751, 756], \"fullname\": \"扬州市\", \"id\": \"321000\", \"location\": { \"lat\": 32.39358, \"lng\": 119.41269 }, \"name\": \"扬州\", \"pinyin\": [\"yang\", \"zhou\"] }, { \"cidx\": [757, 762], \"fullname\": \"镇江市\", \"id\": \"321100\", \"location\": { \"lat\": 32.18959, \"lng\": 119.425 }, \"name\": \"镇江\", \"pinyin\": [\"zhen\", \"jiang\"] }, { \"cidx\": [763, 768], \"fullname\": \"泰州市\", \"id\": \"321200\", \"location\": { \"lat\": 32.45546, \"lng\": 119.92554 }, \"name\": \"泰州\", \"pinyin\": [\"tai\", \"zhou\"] }, { \"cidx\": [769, 773], \"fullname\": \"宿迁市\", \"id\": \"321300\", \"location\": { \"lat\": 33.96193, \"lng\": 118.27549 }, \"name\": \"宿迁\", \"pinyin\": [\"su\", \"qian\"] }, { \"cidx\": [774, 786], \"fullname\": \"杭州市\", \"id\": \"330100\", \"location\": { \"lat\": 30.27415, \"lng\": 120.15515 }, \"name\": \"杭州\", \"pinyin\": [\"hang\", \"zhou\"] }, { \"cidx\": [787, 796], \"fullname\": \"宁波市\", \"id\": \"330200\", \"location\": { \"lat\": 29.87386, \"lng\": 121.55027 }, \"name\": \"宁波\", \"pinyin\": [\"ning\", \"bo\"] }, { \"cidx\": [797, 808], \"fullname\": \"温州市\", \"id\": \"330300\", \"location\": { \"lat\": 27.99492, \"lng\": 120.69939 }, \"name\": \"温州\", \"pinyin\": [\"wen\", \"zhou\"] }, { \"cidx\": [809, 815], \"fullname\": \"嘉兴市\", \"id\": \"330400\", \"location\": { \"lat\": 30.74501, \"lng\": 120.7555 }, \"name\": \"嘉兴\", \"pinyin\": [\"jia\", \"xing\"] }, { \"cidx\": [816, 820], \"fullname\": \"湖州市\", \"id\": \"330500\", \"location\": { \"lat\": 30.89305, \"lng\": 120.08805 }, \"name\": \"湖州\", \"pinyin\": [\"hu\", \"zhou\"] }, { \"cidx\": [821, 826], \"fullname\": \"绍兴市\", \"id\": \"330600\", \"location\": { \"lat\": 30.03033, \"lng\": 120.5802 }, \"name\": \"绍兴\", \"pinyin\": [\"shao\", \"xing\"] }, { \"cidx\": [827, 835], \"fullname\": \"金华市\", \"id\": \"330700\", \"location\": { \"lat\": 29.07812, \"lng\": 119.64759 }, \"name\": \"金华\", \"pinyin\": [\"jin\", \"hua\"] }, { \"cidx\": [836, 841], \"fullname\": \"衢州市\", \"id\": \"330800\", \"location\": { \"lat\": 28.93592, \"lng\": 118.87419 }, \"name\": \"衢州\", \"pinyin\": [\"qu\", \"zhou\"] }, { \"cidx\": [842, 845], \"fullname\": \"舟山市\", \"id\": \"330900\", \"location\": { \"lat\": 29.98539, \"lng\": 122.20778 }, \"name\": \"舟山\", \"pinyin\": [\"zhou\", \"shan\"] }, { \"cidx\": [846, 854], \"fullname\": \"台州市\", \"id\": \"331000\", \"location\": { \"lat\": 28.65611, \"lng\": 121.42056 }, \"name\": \"台州\", \"pinyin\": [\"tai\", \"zhou\"] }, { \"cidx\": [855, 863], \"fullname\": \"丽水市\", \"id\": \"331100\", \"location\": { \"lat\": 28.4672, \"lng\": 119.92293 }, \"name\": \"丽水\", \"pinyin\": [\"li\", \"shui\"] }, { \"cidx\": [864, 872], \"fullname\": \"合肥市\", \"id\": \"340100\", \"location\": { \"lat\": 31.82057, \"lng\": 117.22901 }, \"name\": \"合肥\", \"pinyin\": [\"he\", \"fei\"] }, { \"cidx\": [873, 880], \"fullname\": \"芜湖市\", \"id\": \"340200\", \"location\": { \"lat\": 31.35246, \"lng\": 118.43313 }, \"name\": \"芜湖\", \"pinyin\": [\"wu\", \"hu\"] }, { \"cidx\": [881, 887], \"fullname\": \"蚌埠市\", \"id\": \"340300\", \"location\": { \"lat\": 32.91548, \"lng\": 117.38932 }, \"name\": \"蚌埠\", \"pinyin\": [\"beng\", \"bu\"] }, { \"cidx\": [888, 894], \"fullname\": \"淮南市\", \"id\": \"340400\", \"location\": { \"lat\": 32.62549, \"lng\": 116.9998 }, \"name\": \"淮南\", \"pinyin\": [\"huai\", \"nan\"] }, { \"cidx\": [895, 900], \"fullname\": \"马鞍山市\", \"id\": \"340500\", \"location\": { \"lat\": 31.67067, \"lng\": 118.50611 }, \"name\": \"马鞍山\", \"pinyin\": [\"ma\", \"an\", \"shan\"] }, { \"cidx\": [901, 904], \"fullname\": \"淮北市\", \"id\": \"340600\", \"location\": { \"lat\": 33.95479, \"lng\": 116.79834 }, \"name\": \"淮北\", \"pinyin\": [\"huai\", \"bei\"] }, { \"cidx\": [905, 908], \"fullname\": \"铜陵市\", \"id\": \"340700\", \"location\": { \"lat\": 30.94486, \"lng\": 117.81232 }, \"name\": \"铜陵\", \"pinyin\": [\"tong\", \"ling\"] }, { \"cidx\": [909, 918], \"fullname\": \"安庆市\", \"id\": \"340800\", \"location\": { \"lat\": 30.54294, \"lng\": 117.06354 }, \"name\": \"安庆\", \"pinyin\": [\"an\", \"qing\"] }, { \"cidx\": [919, 925], \"fullname\": \"黄山市\", \"id\": \"341000\", \"location\": { \"lat\": 29.71517, \"lng\": 118.33866 }, \"name\": \"黄山\", \"pinyin\": [\"huang\", \"shan\"] }, { \"cidx\": [926, 933], \"fullname\": \"滁州市\", \"id\": \"341100\", \"location\": { \"lat\": 32.30181, \"lng\": 118.31683 }, \"name\": \"滁州\", \"pinyin\": [\"chu\", \"zhou\"] }, { \"cidx\": [934, 941], \"fullname\": \"阜阳市\", \"id\": \"341200\", \"location\": { \"lat\": 32.88963, \"lng\": 115.81495 }, \"name\": \"阜阳\", \"pinyin\": [\"fu\", \"yang\"] }, { \"cidx\": [942, 946], \"fullname\": \"宿州市\", \"id\": \"341300\", \"location\": { \"lat\": 33.64614, \"lng\": 116.96391 }, \"name\": \"宿州\", \"pinyin\": [\"su\", \"zhou\"] }, { \"cidx\": [947, 953], \"fullname\": \"六安市\", \"id\": \"341500\", \"location\": { \"lat\": 31.73488, \"lng\": 116.52324 }, \"name\": \"六安\", \"pinyin\": [\"liu\", \"an\"] }, { \"cidx\": [954, 957], \"fullname\": \"亳州市\", \"id\": \"341600\", \"location\": { \"lat\": 33.84461, \"lng\": 115.77931 }, \"name\": \"亳州\", \"pinyin\": [\"bo\", \"zhou\"] }, { \"cidx\": [958, 961], \"fullname\": \"池州市\", \"id\": \"341700\", \"location\": { \"lat\": 30.66469, \"lng\": 117.49142 }, \"name\": \"池州\", \"pinyin\": [\"chi\", \"zhou\"] }, { \"cidx\": [962, 968], \"fullname\": \"宣城市\", \"id\": \"341800\", \"location\": { \"lat\": 30.94078, \"lng\": 118.75866 }, \"name\": \"宣城\", \"pinyin\": [\"xuan\", \"cheng\"] }, { \"cidx\": [969, 981], \"fullname\": \"福州市\", \"id\": \"350100\", \"location\": { \"lat\": 26.07421, \"lng\": 119.29647 }, \"name\": \"福州\", \"pinyin\": [\"fu\", \"zhou\"] }, { \"cidx\": [982, 987], \"fullname\": \"厦门市\", \"id\": \"350200\", \"location\": { \"lat\": 24.47951, \"lng\": 118.08948 }, \"name\": \"厦门\", \"pinyin\": [\"xia\", \"men\"] }, { \"cidx\": [988, 992], \"fullname\": \"莆田市\", \"id\": \"350300\", \"location\": { \"lat\": 25.454, \"lng\": 119.00771 }, \"name\": \"莆田\", \"pinyin\": [\"pu\", \"tian\"] }, { \"cidx\": [993, 1004], \"fullname\": \"三明市\", \"id\": \"350400\", \"location\": { \"lat\": 26.26385, \"lng\": 117.63922 }, \"name\": \"三明\", \"pinyin\": [\"san\", \"ming\"] }, { \"cidx\": [1005, 1016], \"fullname\": \"泉州市\", \"id\": \"350500\", \"location\": { \"lat\": 24.87389, \"lng\": 118.67587 }, \"name\": \"泉州\", \"pinyin\": [\"quan\", \"zhou\"] }, { \"cidx\": [1017, 1027], \"fullname\": \"漳州市\", \"id\": \"350600\", \"location\": { \"lat\": 24.51347, \"lng\": 117.64725 }, \"name\": \"漳州\", \"pinyin\": [\"zhang\", \"zhou\"] }, { \"cidx\": [1028, 1037], \"fullname\": \"南平市\", \"id\": \"350700\", \"location\": { \"lat\": 27.33175, \"lng\": 118.12043 }, \"name\": \"南平\", \"pinyin\": [\"nan\", \"ping\"] }, { \"cidx\": [1038, 1044], \"fullname\": \"龙岩市\", \"id\": \"350800\", \"location\": { \"lat\": 25.07504, \"lng\": 117.01722 }, \"name\": \"龙岩\", \"pinyin\": [\"long\", \"yan\"] }, { \"cidx\": [1045, 1053], \"fullname\": \"宁德市\", \"id\": \"350900\", \"location\": { \"lat\": 26.66571, \"lng\": 119.54819 }, \"name\": \"宁德\", \"pinyin\": [\"ning\", \"de\"] }, { \"cidx\": [1054, 1062], \"fullname\": \"南昌市\", \"id\": \"360100\", \"location\": { \"lat\": 28.68202, \"lng\": 115.85794 }, \"name\": \"南昌\", \"pinyin\": [\"nan\", \"chang\"] }, { \"cidx\": [1063, 1066], \"fullname\": \"景德镇市\", \"id\": \"360200\", \"location\": { \"lat\": 29.26869, \"lng\": 117.17839 }, \"name\": \"景德镇\", \"pinyin\": [\"jing\", \"de\", \"zhen\"] }, { \"cidx\": [1067, 1071], \"fullname\": \"萍乡市\", \"id\": \"360300\", \"location\": { \"lat\": 27.62289, \"lng\": 113.85427 }, \"name\": \"萍乡\", \"pinyin\": [\"ping\", \"xiang\"] }, { \"cidx\": [1072, 1084], \"fullname\": \"九江市\", \"id\": \"360400\", \"location\": { \"lat\": 29.70548, \"lng\": 116.00146 }, \"name\": \"九江\", \"pinyin\": [\"jiu\", \"jiang\"] }, { \"cidx\": [1085, 1086], \"fullname\": \"新余市\", \"id\": \"360500\", \"location\": { \"lat\": 27.81776, \"lng\": 114.91713 }, \"name\": \"新余\", \"pinyin\": [\"xin\", \"yu\"] }, { \"cidx\": [1087, 1089], \"fullname\": \"鹰潭市\", \"id\": \"360600\", \"location\": { \"lat\": 28.26019, \"lng\": 117.06919 }, \"name\": \"鹰潭\", \"pinyin\": [\"ying\", \"tan\"] }, { \"cidx\": [1090, 1107], \"fullname\": \"赣州市\", \"id\": \"360700\", \"location\": { \"lat\": 25.83109, \"lng\": 114.93476 }, \"name\": \"赣州\", \"pinyin\": [\"gan\", \"zhou\"] }, { \"cidx\": [1108, 1120], \"fullname\": \"吉安市\", \"id\": \"360800\", \"location\": { \"lat\": 27.11382, \"lng\": 114.99376 }, \"name\": \"吉安\", \"pinyin\": [\"ji\", \"an\"] }, { \"cidx\": [1121, 1130], \"fullname\": \"宜春市\", \"id\": \"360900\", \"location\": { \"lat\": 27.81443, \"lng\": 114.41612 }, \"name\": \"宜春\", \"pinyin\": [\"yi\", \"chun\"] }, { \"cidx\": [1131, 1141], \"fullname\": \"抚州市\", \"id\": \"361000\", \"location\": { \"lat\": 27.94781, \"lng\": 116.35809 }, \"name\": \"抚州\", \"pinyin\": [\"fu\", \"zhou\"] }, { \"cidx\": [1142, 1153], \"fullname\": \"上饶市\", \"id\": \"361100\", \"location\": { \"lat\": 28.45463, \"lng\": 117.94357 }, \"name\": \"上饶\", \"pinyin\": [\"shang\", \"rao\"] }, { \"cidx\": [1154, 1165], \"fullname\": \"济南市\", \"id\": \"370100\", \"location\": { \"lat\": 36.65184, \"lng\": 117.12009 }, \"name\": \"济南\", \"pinyin\": [\"ji\", \"nan\"] }, { \"cidx\": [1166, 1175], \"fullname\": \"青岛市\", \"id\": \"370200\", \"location\": { \"lat\": 36.06623, \"lng\": 120.38299 }, \"name\": \"青岛\", \"pinyin\": [\"qing\", \"dao\"] }, { \"cidx\": [1176, 1183], \"fullname\": \"淄博市\", \"id\": \"370300\", \"location\": { \"lat\": 36.8131, \"lng\": 118.0548 }, \"name\": \"淄博\", \"pinyin\": [\"zi\", \"bo\"] }, { \"cidx\": [1184, 1189], \"fullname\": \"枣庄市\", \"id\": \"370400\", \"location\": { \"lat\": 34.81071, \"lng\": 117.32196 }, \"name\": \"枣庄\", \"pinyin\": [\"zao\", \"zhuang\"] }, { \"cidx\": [1190, 1194], \"fullname\": \"东营市\", \"id\": \"370500\", \"location\": { \"lat\": 37.43365, \"lng\": 118.67466 }, \"name\": \"东营\", \"pinyin\": [\"dong\", \"ying\"] }, { \"cidx\": [1195, 1206], \"fullname\": \"烟台市\", \"id\": \"370600\", \"location\": { \"lat\": 37.46353, \"lng\": 121.44801 }, \"name\": \"烟台\", \"pinyin\": [\"yan\", \"tai\"] }, { \"cidx\": [1207, 1218], \"fullname\": \"潍坊市\", \"id\": \"370700\", \"location\": { \"lat\": 36.70686, \"lng\": 119.16176 }, \"name\": \"潍坊\", \"pinyin\": [\"wei\", \"fang\"] }, { \"cidx\": [1219, 1229], \"fullname\": \"济宁市\", \"id\": \"370800\", \"location\": { \"lat\": 35.41459, \"lng\": 116.58724 }, \"name\": \"济宁\", \"pinyin\": [\"ji\", \"ning\"] }, { \"cidx\": [1230, 1235], \"fullname\": \"泰安市\", \"id\": \"370900\", \"location\": { \"lat\": 36.19994, \"lng\": 117.0884 }, \"name\": \"泰安\", \"pinyin\": [\"tai\", \"an\"] }, { \"cidx\": [1236, 1239], \"fullname\": \"威海市\", \"id\": \"371000\", \"location\": { \"lat\": 37.51348, \"lng\": 122.12171 }, \"name\": \"威海\", \"pinyin\": [\"wei\", \"hai\"] }, { \"cidx\": [1240, 1243], \"fullname\": \"日照市\", \"id\": \"371100\", \"location\": { \"lat\": 35.41646, \"lng\": 119.52719 }, \"name\": \"日照\", \"pinyin\": [\"ri\", \"zhao\"] }, { \"cidx\": [1244, 1255], \"fullname\": \"临沂市\", \"id\": \"371300\", \"location\": { \"lat\": 35.10465, \"lng\": 118.35646 }, \"name\": \"临沂\", \"pinyin\": [\"lin\", \"yi\"] }, { \"cidx\": [1256, 1266], \"fullname\": \"德州市\", \"id\": \"371400\", \"location\": { \"lat\": 37.4355, \"lng\": 116.35927 }, \"name\": \"德州\", \"pinyin\": [\"de\", \"zhou\"] }, { \"cidx\": [1267, 1274], \"fullname\": \"聊城市\", \"id\": \"371500\", \"location\": { \"lat\": 36.45702, \"lng\": 115.98549 }, \"name\": \"聊城\", \"pinyin\": [\"liao\", \"cheng\"] }, { \"cidx\": [1275, 1281], \"fullname\": \"滨州市\", \"id\": \"371600\", \"location\": { \"lat\": 37.38211, \"lng\": 117.97279 }, \"name\": \"滨州\", \"pinyin\": [\"bin\", \"zhou\"] }, { \"cidx\": [1282, 1290], \"fullname\": \"菏泽市\", \"id\": \"371700\", \"location\": { \"lat\": 35.23363, \"lng\": 115.48115 }, \"name\": \"菏泽\", \"pinyin\": [\"he\", \"ze\"] }, { \"cidx\": [1291, 1302], \"fullname\": \"郑州市\", \"id\": \"410100\", \"location\": { \"lat\": 34.74725, \"lng\": 113.62493 }, \"name\": \"郑州\", \"pinyin\": [\"zheng\", \"zhou\"] }, { \"cidx\": [1303, 1311], \"fullname\": \"开封市\", \"id\": \"410200\", \"location\": { \"lat\": 34.79726, \"lng\": 114.30731 }, \"name\": \"开封\", \"pinyin\": [\"kai\", \"feng\"] }, { \"cidx\": [1312, 1326], \"fullname\": \"洛阳市\", \"id\": \"410300\", \"location\": { \"lat\": 34.61812, \"lng\": 112.45361 }, \"name\": \"洛阳\", \"pinyin\": [\"luo\", \"yang\"] }, { \"cidx\": [1327, 1336], \"fullname\": \"平顶山市\", \"id\": \"410400\", \"location\": { \"lat\": 33.76609, \"lng\": 113.19241 }, \"name\": \"平顶山\", \"pinyin\": [\"ping\", \"ding\", \"shan\"] }, { \"cidx\": [1337, 1345], \"fullname\": \"安阳市\", \"id\": \"410500\", \"location\": { \"lat\": 36.09771, \"lng\": 114.3931 }, \"name\": \"安阳\", \"pinyin\": [\"an\", \"yang\"] }, { \"cidx\": [1346, 1350], \"fullname\": \"鹤壁市\", \"id\": \"410600\", \"location\": { \"lat\": 35.747, \"lng\": 114.29745 }, \"name\": \"鹤壁\", \"pinyin\": [\"he\", \"bi\"] }, { \"cidx\": [1351, 1362], \"fullname\": \"新乡市\", \"id\": \"410700\", \"location\": { \"lat\": 35.30323, \"lng\": 113.92675 }, \"name\": \"新乡\", \"pinyin\": [\"xin\", \"xiang\"] }, { \"cidx\": [1363, 1372], \"fullname\": \"焦作市\", \"id\": \"410800\", \"location\": { \"lat\": 35.21563, \"lng\": 113.24201 }, \"name\": \"焦作\", \"pinyin\": [\"jiao\", \"zuo\"] }, { \"cidx\": [1373, 1378], \"fullname\": \"濮阳市\", \"id\": \"410900\", \"location\": { \"lat\": 35.76189, \"lng\": 115.02932 }, \"name\": \"濮阳\", \"pinyin\": [\"pu\", \"yang\"] }, { \"cidx\": [1379, 1384], \"fullname\": \"许昌市\", \"id\": \"411000\", \"location\": { \"lat\": 34.0357, \"lng\": 113.85233 }, \"name\": \"许昌\", \"pinyin\": [\"xu\", \"chang\"] }, { \"cidx\": [1385, 1389], \"fullname\": \"漯河市\", \"id\": \"411100\", \"location\": { \"lat\": 33.58149, \"lng\": 114.01681 }, \"name\": \"漯河\", \"pinyin\": [\"luo\", \"he\"] }, { \"cidx\": [1390, 1395], \"fullname\": \"三门峡市\", \"id\": \"411200\", \"location\": { \"lat\": 34.77261, \"lng\": 111.2003 }, \"name\": \"三门峡\", \"pinyin\": [\"san\", \"men\", \"xia\"] }, { \"cidx\": [1396, 1408], \"fullname\": \"南阳市\", \"id\": \"411300\", \"location\": { \"lat\": 32.99073, \"lng\": 112.52851 }, \"name\": \"南阳\", \"pinyin\": [\"nan\", \"yang\"] }, { \"cidx\": [1409, 1417], \"fullname\": \"商丘市\", \"id\": \"411400\", \"location\": { \"lat\": 34.41427, \"lng\": 115.65635 }, \"name\": \"商丘\", \"pinyin\": [\"shang\", \"qiu\"] }, { \"cidx\": [1418, 1427], \"fullname\": \"信阳市\", \"id\": \"411500\", \"location\": { \"lat\": 32.14714, \"lng\": 114.09279 }, \"name\": \"信阳\", \"pinyin\": [\"xin\", \"yang\"] }, { \"cidx\": [1428, 1437], \"fullname\": \"周口市\", \"id\": \"411600\", \"location\": { \"lat\": 33.62583, \"lng\": 114.69695 }, \"name\": \"周口\", \"pinyin\": [\"zhou\", \"kou\"] }, { \"cidx\": [1438, 1447], \"fullname\": \"驻马店市\", \"id\": \"411700\", \"location\": { \"lat\": 33.01142, \"lng\": 114.02299 }, \"name\": \"驻马店\", \"pinyin\": [\"zhu\", \"ma\", \"dian\"] }, { \"fullname\": \"济源市\", \"id\": \"419001\", \"location\": { \"lat\": 35.06707, \"lng\": 112.60273 }, \"name\": \"济源\", \"pinyin\": [\"ji\", \"yuan\"] }, { \"cidx\": [1448, 1460], \"fullname\": \"武汉市\", \"id\": \"420100\", \"location\": { \"lat\": 30.59276, \"lng\": 114.30525 }, \"name\": \"武汉\", \"pinyin\": [\"wu\", \"han\"] }, { \"cidx\": [1461, 1466], \"fullname\": \"黄石市\", \"id\": \"420200\", \"location\": { \"lat\": 30.19953, \"lng\": 115.0389 }, \"name\": \"黄石\", \"pinyin\": [\"huang\", \"shi\"] }, { \"cidx\": [1467, 1474], \"fullname\": \"十堰市\", \"id\": \"420300\", \"location\": { \"lat\": 32.62918, \"lng\": 110.79801 }, \"name\": \"十堰\", \"pinyin\": [\"shi\", \"yan\"] }, { \"cidx\": [1475, 1487], \"fullname\": \"宜昌市\", \"id\": \"420500\", \"location\": { \"lat\": 30.69186, \"lng\": 111.28642 }, \"name\": \"宜昌\", \"pinyin\": [\"yi\", \"chang\"] }, { \"cidx\": [1488, 1496], \"fullname\": \"襄阳市\", \"id\": \"420600\", \"location\": { \"lat\": 32.009, \"lng\": 112.12255 }, \"name\": \"襄阳\", \"pinyin\": [\"xiang\", \"yang\"] }, { \"cidx\": [1497, 1499], \"fullname\": \"鄂州市\", \"id\": \"420700\", \"location\": { \"lat\": 30.39085, \"lng\": 114.89495 }, \"name\": \"鄂州\", \"pinyin\": [\"e\", \"zhou\"] }, { \"cidx\": [1500, 1504], \"fullname\": \"荆门市\", \"id\": \"420800\", \"location\": { \"lat\": 31.03546, \"lng\": 112.19945 }, \"name\": \"荆门\", \"pinyin\": [\"jing\", \"men\"] }, { \"cidx\": [1505, 1511], \"fullname\": \"孝感市\", \"id\": \"420900\", \"location\": { \"lat\": 30.92483, \"lng\": 113.91645 }, \"name\": \"孝感\", \"pinyin\": [\"xiao\", \"gan\"] }, { \"cidx\": [1512, 1519], \"fullname\": \"荆州市\", \"id\": \"421000\", \"location\": { \"lat\": 30.33479, \"lng\": 112.24069 }, \"name\": \"荆州\", \"pinyin\": [\"jing\", \"zhou\"] }, { \"cidx\": [1520, 1529], \"fullname\": \"黄冈市\", \"id\": \"421100\", \"location\": { \"lat\": 30.45347, \"lng\": 114.87238 }, \"name\": \"黄冈\", \"pinyin\": [\"huang\", \"gang\"] }, { \"cidx\": [1530, 1535], \"fullname\": \"咸宁市\", \"id\": \"421200\", \"location\": { \"lat\": 29.84126, \"lng\": 114.32245 }, \"name\": \"咸宁\", \"pinyin\": [\"xian\", \"ning\"] }, { \"cidx\": [1536, 1538], \"fullname\": \"随州市\", \"id\": \"421300\", \"location\": { \"lat\": 31.69013, \"lng\": 113.38262 }, \"name\": \"随州\", \"pinyin\": [\"sui\", \"zhou\"] }, { \"cidx\": [1539, 1546], \"fullname\": \"恩施土家族苗族自治州\", \"id\": \"422800\", \"location\": { \"lat\": 30.27217, \"lng\": 109.48817 }, \"name\": \"恩施\", \"pinyin\": [\"en\", \"shi\"] }, { \"fullname\": \"仙桃市\", \"id\": \"429004\", \"location\": { \"lat\": 30.36251, \"lng\": 113.4545 }, \"name\": \"仙桃\", \"pinyin\": [\"xian\", \"tao\"] }, { \"fullname\": \"潜江市\", \"id\": \"429005\", \"location\": { \"lat\": 30.40147, \"lng\": 112.8993 }, \"name\": \"潜江\", \"pinyin\": [\"qian\", \"jiang\"] }, { \"fullname\": \"天门市\", \"id\": \"429006\", \"location\": { \"lat\": 30.66339, \"lng\": 113.16614 }, \"name\": \"天门\", \"pinyin\": [\"tian\", \"men\"] }, { \"fullname\": \"神农架林区\", \"id\": \"429021\", \"location\": { \"lat\": 31.74452, \"lng\": 110.67598 }, \"name\": \"神农架\", \"pinyin\": [\"shen\", \"nong\", \"jia\"] }, { \"cidx\": [1547, 1555], \"fullname\": \"长沙市\", \"id\": \"430100\", \"location\": { \"lat\": 28.22778, \"lng\": 112.93886 }, \"name\": \"长沙\", \"pinyin\": [\"chang\", \"sha\"] }, { \"cidx\": [1556, 1564], \"fullname\": \"株洲市\", \"id\": \"430200\", \"location\": { \"lat\": 27.82767, \"lng\": 113.13396 }, \"name\": \"株洲\", \"pinyin\": [\"zhu\", \"zhou\"] }, { \"cidx\": [1565, 1569], \"fullname\": \"湘潭市\", \"id\": \"430300\", \"location\": { \"lat\": 27.82975, \"lng\": 112.94411 }, \"name\": \"湘潭\", \"pinyin\": [\"xiang\", \"tan\"] }, { \"cidx\": [1570, 1581], \"fullname\": \"衡阳市\", \"id\": \"430400\", \"location\": { \"lat\": 26.89324, \"lng\": 112.57195 }, \"name\": \"衡阳\", \"pinyin\": [\"heng\", \"yang\"] }, { \"cidx\": [1582, 1593], \"fullname\": \"邵阳市\", \"id\": \"430500\", \"location\": { \"lat\": 27.2389, \"lng\": 111.4677 }, \"name\": \"邵阳\", \"pinyin\": [\"shao\", \"yang\"] }, { \"cidx\": [1594, 1602], \"fullname\": \"岳阳市\", \"id\": \"430600\", \"location\": { \"lat\": 29.35728, \"lng\": 113.12919 }, \"name\": \"岳阳\", \"pinyin\": [\"yue\", \"yang\"] }, { \"cidx\": [1603, 1611], \"fullname\": \"常德市\", \"id\": \"430700\", \"location\": { \"lat\": 29.03158, \"lng\": 111.69854 }, \"name\": \"常德\", \"pinyin\": [\"chang\", \"de\"] }, { \"cidx\": [1612, 1615], \"fullname\": \"张家界市\", \"id\": \"430800\", \"location\": { \"lat\": 29.11667, \"lng\": 110.47839 }, \"name\": \"张家界\", \"pinyin\": [\"zhang\", \"jia\", \"jie\"] }, { \"cidx\": [1616, 1621], \"fullname\": \"益阳市\", \"id\": \"430900\", \"location\": { \"lat\": 28.55391, \"lng\": 112.35516 }, \"name\": \"益阳\", \"pinyin\": [\"yi\", \"yang\"] }, { \"cidx\": [1622, 1632], \"fullname\": \"郴州市\", \"id\": \"431000\", \"location\": { \"lat\": 25.77063, \"lng\": 113.01485 }, \"name\": \"郴州\", \"pinyin\": [\"chen\", \"zhou\"] }, { \"cidx\": [1633, 1643], \"fullname\": \"永州市\", \"id\": \"431100\", \"location\": { \"lat\": 26.42034, \"lng\": 111.61225 }, \"name\": \"永州\", \"pinyin\": [\"yong\", \"zhou\"] }, { \"cidx\": [1644, 1655], \"fullname\": \"怀化市\", \"id\": \"431200\", \"location\": { \"lat\": 27.56974, \"lng\": 110.0016 }, \"name\": \"怀化\", \"pinyin\": [\"huai\", \"hua\"] }, { \"cidx\": [1656, 1660], \"fullname\": \"娄底市\", \"id\": \"431300\", \"location\": { \"lat\": 27.69728, \"lng\": 111.99458 }, \"name\": \"娄底\", \"pinyin\": [\"lou\", \"di\"] }, { \"cidx\": [1661, 1668], \"fullname\": \"湘西土家族苗族自治州\", \"id\": \"433100\", \"location\": { \"lat\": 28.31173, \"lng\": 109.73893 }, \"name\": \"湘西\", \"pinyin\": [\"xiang\", \"xi\"] }, { \"cidx\": [1669, 1679], \"fullname\": \"广州市\", \"id\": \"440100\", \"location\": { \"lat\": 23.12908, \"lng\": 113.26436 }, \"name\": \"广州\", \"pinyin\": [\"guang\", \"zhou\"] }, { \"cidx\": [1680, 1689], \"fullname\": \"韶关市\", \"id\": \"440200\", \"location\": { \"lat\": 24.81039, \"lng\": 113.59723 }, \"name\": \"韶关\", \"pinyin\": [\"shao\", \"guan\"] }, { \"cidx\": [1690, 1698], \"fullname\": \"深圳市\", \"id\": \"440300\", \"location\": { \"lat\": 22.54286, \"lng\": 114.05956 }, \"name\": \"深圳\", \"pinyin\": [\"shen\", \"zhen\"] }, { \"cidx\": [1699, 1702], \"fullname\": \"珠海市\", \"id\": \"440400\", \"location\": { \"lat\": 22.27073, \"lng\": 113.57668 }, \"name\": \"珠海\", \"pinyin\": [\"zhu\", \"hai\"] }, { \"cidx\": [1703, 1709], \"fullname\": \"汕头市\", \"id\": \"440500\", \"location\": { \"lat\": 23.3535, \"lng\": 116.68221 }, \"name\": \"汕头\", \"pinyin\": [\"shan\", \"tou\"] }, { \"cidx\": [1710, 1714], \"fullname\": \"佛山市\", \"id\": \"440600\", \"location\": { \"lat\": 23.02185, \"lng\": 113.12192 }, \"name\": \"佛山\", \"pinyin\": [\"fo\", \"shan\"] }, { \"cidx\": [1715, 1721], \"fullname\": \"江门市\", \"id\": \"440700\", \"location\": { \"lat\": 22.57865, \"lng\": 113.08161 }, \"name\": \"江门\", \"pinyin\": [\"jiang\", \"men\"] }, { \"cidx\": [1722, 1730], \"fullname\": \"湛江市\", \"id\": \"440800\", \"location\": { \"lat\": 21.27134, \"lng\": 110.35894 }, \"name\": \"湛江\", \"pinyin\": [\"zhan\", \"jiang\"] }, { \"cidx\": [1731, 1735], \"fullname\": \"茂名市\", \"id\": \"440900\", \"location\": { \"lat\": 21.66329, \"lng\": 110.92523 }, \"name\": \"茂名\", \"pinyin\": [\"mao\", \"ming\"] }, { \"cidx\": [1736, 1743], \"fullname\": \"肇庆市\", \"id\": \"441200\", \"location\": { \"lat\": 23.0469, \"lng\": 112.46528 }, \"name\": \"肇庆\", \"pinyin\": [\"zhao\", \"qing\"] }, { \"cidx\": [1744, 1748], \"fullname\": \"惠州市\", \"id\": \"441300\", \"location\": { \"lat\": 23.11075, \"lng\": 114.41679 }, \"name\": \"惠州\", \"pinyin\": [\"hui\", \"zhou\"] }, { \"cidx\": [1749, 1756], \"fullname\": \"梅州市\", \"id\": \"441400\", \"location\": { \"lat\": 24.28844, \"lng\": 116.12264 }, \"name\": \"梅州\", \"pinyin\": [\"mei\", \"zhou\"] }, { \"cidx\": [1757, 1760], \"fullname\": \"汕尾市\", \"id\": \"441500\", \"location\": { \"lat\": 22.78566, \"lng\": 115.37514 }, \"name\": \"汕尾\", \"pinyin\": [\"shan\", \"wei\"] }, { \"cidx\": [1761, 1766], \"fullname\": \"河源市\", \"id\": \"441600\", \"location\": { \"lat\": 23.74365, \"lng\": 114.70065 }, \"name\": \"河源\", \"pinyin\": [\"he\", \"yuan\"] }, { \"cidx\": [1767, 1770], \"fullname\": \"阳江市\", \"id\": \"441700\", \"location\": { \"lat\": 21.85829, \"lng\": 111.98256 }, \"name\": \"阳江\", \"pinyin\": [\"yang\", \"jiang\"] }, { \"cidx\": [1771, 1778], \"fullname\": \"清远市\", \"id\": \"441800\", \"location\": { \"lat\": 23.68201, \"lng\": 113.05615 }, \"name\": \"清远\", \"pinyin\": [\"qing\", \"yuan\"] }, { \"cidx\": [1779, 1779], \"fullname\": \"东莞市\", \"id\": \"441900\", \"location\": { \"lat\": 23.02067, \"lng\": 113.75179 }, \"name\": \"东莞\", \"pinyin\": [\"dong\", \"guan\"] }, { \"cidx\": [1780, 1780], \"fullname\": \"中山市\", \"id\": \"442000\", \"location\": { \"lat\": 22.51595, \"lng\": 113.3926 }, \"name\": \"中山\", \"pinyin\": [\"zhong\", \"shan\"] }, { \"cidx\": [1781, 1783], \"fullname\": \"潮州市\", \"id\": \"445100\", \"location\": { \"lat\": 23.6567, \"lng\": 116.62296 }, \"name\": \"潮州\", \"pinyin\": [\"chao\", \"zhou\"] }, { \"cidx\": [1784, 1788], \"fullname\": \"揭阳市\", \"id\": \"445200\", \"location\": { \"lat\": 23.54972, \"lng\": 116.37271 }, \"name\": \"揭阳\", \"pinyin\": [\"jie\", \"yang\"] }, { \"cidx\": [1789, 1793], \"fullname\": \"云浮市\", \"id\": \"445300\", \"location\": { \"lat\": 22.91525, \"lng\": 112.04453 }, \"name\": \"云浮\", \"pinyin\": [\"yun\", \"fu\"] }, { \"cidx\": [1794, 1805], \"fullname\": \"南宁市\", \"id\": \"450100\", \"location\": { \"lat\": 22.81673, \"lng\": 108.3669 }, \"name\": \"南宁\", \"pinyin\": [\"nan\", \"ning\"] }, { \"cidx\": [1806, 1815], \"fullname\": \"柳州市\", \"id\": \"450200\", \"location\": { \"lat\": 24.32543, \"lng\": 109.41552 }, \"name\": \"柳州\", \"pinyin\": [\"liu\", \"zhou\"] }, { \"cidx\": [1816, 1832], \"fullname\": \"桂林市\", \"id\": \"450300\", \"location\": { \"lat\": 25.27361, \"lng\": 110.29002 }, \"name\": \"桂林\", \"pinyin\": [\"gui\", \"lin\"] }, { \"cidx\": [1833, 1839], \"fullname\": \"梧州市\", \"id\": \"450400\", \"location\": { \"lat\": 23.47691, \"lng\": 111.27917 }, \"name\": \"梧州\", \"pinyin\": [\"wu\", \"zhou\"] }, { \"cidx\": [1840, 1843], \"fullname\": \"北海市\", \"id\": \"450500\", \"location\": { \"lat\": 21.48112, \"lng\": 109.12008 }, \"name\": \"北海\", \"pinyin\": [\"bei\", \"hai\"] }, { \"cidx\": [1844, 1847], \"fullname\": \"防城港市\", \"id\": \"450600\", \"location\": { \"lat\": 21.68713, \"lng\": 108.35472 }, \"name\": \"防城港\", \"pinyin\": [\"fang\", \"cheng\", \"gang\"] }, { \"cidx\": [1848, 1851], \"fullname\": \"钦州市\", \"id\": \"450700\", \"location\": { \"lat\": 21.9797, \"lng\": 108.65431 }, \"name\": \"钦州\", \"pinyin\": [\"qin\", \"zhou\"] }, { \"cidx\": [1852, 1856], \"fullname\": \"贵港市\", \"id\": \"450800\", \"location\": { \"lat\": 23.11306, \"lng\": 109.59764 }, \"name\": \"贵港\", \"pinyin\": [\"gui\", \"gang\"] }, { \"cidx\": [1857, 1863], \"fullname\": \"玉林市\", \"id\": \"450900\", \"location\": { \"lat\": 22.65451, \"lng\": 110.18098 }, \"name\": \"玉林\", \"pinyin\": [\"yu\", \"lin\"] }, { \"cidx\": [1864, 1875], \"fullname\": \"百色市\", \"id\": \"451000\", \"location\": { \"lat\": 23.90216, \"lng\": 106.61838 }, \"name\": \"百色\", \"pinyin\": [\"bai\", \"se\"] }, { \"cidx\": [1876, 1880], \"fullname\": \"贺州市\", \"id\": \"451100\", \"location\": { \"lat\": 24.40346, \"lng\": 111.56655 }, \"name\": \"贺州\", \"pinyin\": [\"he\", \"zhou\"] }, { \"cidx\": [1881, 1891], \"fullname\": \"河池市\", \"id\": \"451200\", \"location\": { \"lat\": 24.69291, \"lng\": 108.0854 }, \"name\": \"河池\", \"pinyin\": [\"he\", \"chi\"] }, { \"cidx\": [1892, 1897], \"fullname\": \"来宾市\", \"id\": \"451300\", \"location\": { \"lat\": 23.7521, \"lng\": 109.22238 }, \"name\": \"来宾\", \"pinyin\": [\"lai\", \"bin\"] }, { \"cidx\": [1898, 1904], \"fullname\": \"崇左市\", \"id\": \"451400\", \"location\": { \"lat\": 22.37895, \"lng\": 107.36485 }, \"name\": \"崇左\", \"pinyin\": [\"chong\", \"zuo\"] }, { \"cidx\": [1905, 1908], \"fullname\": \"海口市\", \"id\": \"460100\", \"location\": { \"lat\": 20.04422, \"lng\": 110.19989 }, \"name\": \"海口\", \"pinyin\": [\"hai\", \"kou\"] }, { \"cidx\": [1909, 1912], \"fullname\": \"三亚市\", \"id\": \"460200\", \"location\": { \"lat\": 18.25248, \"lng\": 109.51209 }, \"name\": \"三亚\", \"pinyin\": [\"san\", \"ya\"] }, { \"cidx\": [1913, 1915], \"fullname\": \"三沙市\", \"id\": \"460300\", \"location\": { \"lat\": 16.83272, \"lng\": 112.33356 }, \"name\": \"三沙\", \"pinyin\": [\"san\", \"sha\"] }, { \"cidx\": [1916, 1916], \"fullname\": \"儋州市\", \"id\": \"460400\", \"location\": { \"lat\": 19.52093, \"lng\": 109.58069 }, \"name\": \"儋州\", \"pinyin\": [\"dan\", \"zhou\"] }, { \"fullname\": \"五指山市\", \"id\": \"469001\", \"location\": { \"lat\": 18.77515, \"lng\": 109.51696 }, \"name\": \"五指山\", \"pinyin\": [\"wu\", \"zhi\", \"shan\"] }, { \"fullname\": \"琼海市\", \"id\": \"469002\", \"location\": { \"lat\": 19.25838, \"lng\": 110.47464 }, \"name\": \"琼海\", \"pinyin\": [\"qiong\", \"hai\"] }, { \"fullname\": \"文昌市\", \"id\": \"469005\", \"location\": { \"lat\": 19.54329, \"lng\": 110.79774 }, \"name\": \"文昌\", \"pinyin\": [\"wen\", \"chang\"] }, { \"fullname\": \"万宁市\", \"id\": \"469006\", \"location\": { \"lat\": 18.79532, \"lng\": 110.38975 }, \"name\": \"万宁\", \"pinyin\": [\"wan\", \"ning\"] }, { \"fullname\": \"东方市\", \"id\": \"469007\", \"location\": { \"lat\": 19.09614, \"lng\": 108.65367 }, \"name\": \"东方\", \"pinyin\": [\"dong\", \"fang\"] }, { \"fullname\": \"定安县\", \"id\": \"469021\", \"location\": { \"lat\": 19.68121, \"lng\": 110.3593 }, \"name\": \"定安\", \"pinyin\": [\"ding\", \"an\"] }, { \"fullname\": \"屯昌县\", \"id\": \"469022\", \"location\": { \"lat\": 19.35182, \"lng\": 110.10347 }, \"name\": \"屯昌\", \"pinyin\": [\"tun\", \"chang\"] }, { \"fullname\": \"澄迈县\", \"id\": \"469023\", \"location\": { \"lat\": 19.73849, \"lng\": 110.00487 }, \"name\": \"澄迈\", \"pinyin\": [\"cheng\", \"mai\"] }, { \"fullname\": \"临高县\", \"id\": \"469024\", \"location\": { \"lat\": 19.91243, \"lng\": 109.69077 }, \"name\": \"临高\", \"pinyin\": [\"lin\", \"gao\"] }, { \"fullname\": \"白沙黎族自治县\", \"id\": \"469025\", \"location\": { \"lat\": 19.22543, \"lng\": 109.45167 }, \"name\": \"白沙\", \"pinyin\": [\"bai\", \"sha\"] }, { \"fullname\": \"昌江黎族自治县\", \"id\": \"469026\", \"location\": { \"lat\": 19.29828, \"lng\": 109.05559 }, \"name\": \"昌江\", \"pinyin\": [\"chang\", \"jiang\"] }, { \"fullname\": \"乐东黎族自治县\", \"id\": \"469027\", \"location\": { \"lat\": 18.74986, \"lng\": 109.17361 }, \"name\": \"乐东\", \"pinyin\": [\"le\", \"dong\"] }, { \"fullname\": \"陵水黎族自治县\", \"id\": \"469028\", \"location\": { \"lat\": 18.50596, \"lng\": 110.0372 }, \"name\": \"陵水\", \"pinyin\": [\"ling\", \"shui\"] }, { \"fullname\": \"保亭黎族苗族自治县\", \"id\": \"469029\", \"location\": { \"lat\": 18.63905, \"lng\": 109.70259 }, \"name\": \"保亭\", \"pinyin\": [\"bao\", \"ting\"] }, { \"fullname\": \"琼中黎族苗族自治县\", \"id\": \"469030\", \"location\": { \"lat\": 19.03334, \"lng\": 109.83839 }, \"name\": \"琼中\", \"pinyin\": [\"qiong\", \"zhong\"] }, { \"fullname\": \"万州区\", \"id\": \"500101\", \"location\": { \"lat\": 30.8079, \"lng\": 108.40873 }, \"name\": \"万州\", \"pinyin\": [\"wan\", \"zhou\"] }, { \"fullname\": \"涪陵区\", \"id\": \"500102\", \"location\": { \"lat\": 29.70239, \"lng\": 107.38779 }, \"name\": \"涪陵\", \"pinyin\": [\"fu\", \"ling\"] }, { \"fullname\": \"渝中区\", \"id\": \"500103\", \"location\": { \"lat\": 29.55314, \"lng\": 106.5686 }, \"name\": \"渝中\", \"pinyin\": [\"yu\", \"zhong\"] }, { \"fullname\": \"大渡口区\", \"id\": \"500104\", \"location\": { \"lat\": 29.48408, \"lng\": 106.48225 }, \"name\": \"大渡口\", \"pinyin\": [\"da\", \"du\", \"kou\"] }, { \"fullname\": \"江北区\", \"id\": \"500105\", \"location\": { \"lat\": 29.60661, \"lng\": 106.57439 }, \"name\": \"江北\", \"pinyin\": [\"jiang\", \"bei\"] }, { \"fullname\": \"沙坪坝区\", \"id\": \"500106\", \"location\": { \"lat\": 29.54098, \"lng\": 106.45773 }, \"name\": \"沙坪坝\", \"pinyin\": [\"sha\", \"ping\", \"ba\"] }, { \"fullname\": \"九龙坡区\", \"id\": \"500107\", \"location\": { \"lat\": 29.50207, \"lng\": 106.5114 }, \"name\": \"九龙坡\", \"pinyin\": [\"jiu\", \"long\", \"po\"] }, { \"fullname\": \"南岸区\", \"id\": \"500108\", \"location\": { \"lat\": 29.52168, \"lng\": 106.56256 }, \"name\": \"南岸\", \"pinyin\": [\"nan\", \"an\"] }, { \"fullname\": \"北碚区\", \"id\": \"500109\", \"location\": { \"lat\": 29.80583, \"lng\": 106.39628 }, \"name\": \"北碚\", \"pinyin\": [\"bei\", \"bei\"] }, { \"fullname\": \"綦江区\", \"id\": \"500110\", \"location\": { \"lat\": 28.96463, \"lng\": 106.92852 }, \"name\": \"綦江\", \"pinyin\": [\"qi\", \"jiang\"] }, { \"fullname\": \"大足区\", \"id\": \"500111\", \"location\": { \"lat\": 29.48604, \"lng\": 105.78017 }, \"name\": \"大足\", \"pinyin\": [\"da\", \"zu\"] }, { \"fullname\": \"渝北区\", \"id\": \"500112\", \"location\": { \"lat\": 29.71798, \"lng\": 106.63043 }, \"name\": \"渝北\", \"pinyin\": [\"yu\", \"bei\"] }, { \"fullname\": \"巴南区\", \"id\": \"500113\", \"location\": { \"lat\": 29.40268, \"lng\": 106.54041 }, \"name\": \"巴南\", \"pinyin\": [\"ba\", \"nan\"] }, { \"fullname\": \"黔江区\", \"id\": \"500114\", \"location\": { \"lat\": 29.53322, \"lng\": 108.771086 }, \"name\": \"黔江\", \"pinyin\": [\"qian\", \"jiang\"] }, { \"fullname\": \"长寿区\", \"id\": \"500115\", \"location\": { \"lat\": 29.85781, \"lng\": 107.08105 }, \"name\": \"长寿\", \"pinyin\": [\"chang\", \"shou\"] }, { \"fullname\": \"江津区\", \"id\": \"500116\", \"location\": { \"lat\": 29.29014, \"lng\": 106.25936 }, \"name\": \"江津\", \"pinyin\": [\"jiang\", \"jin\"] }, { \"fullname\": \"合川区\", \"id\": \"500117\", \"location\": { \"lat\": 29.97288, \"lng\": 106.27679 }, \"name\": \"合川\", \"pinyin\": [\"he\", \"chuan\"] }, { \"fullname\": \"永川区\", \"id\": \"500118\", \"location\": { \"lat\": 29.356, \"lng\": 105.92709 }, \"name\": \"永川\", \"pinyin\": [\"yong\", \"chuan\"] }, { \"fullname\": \"南川区\", \"id\": \"500119\", \"location\": { \"lat\": 29.15788, \"lng\": 107.09896 }, \"name\": \"南川\", \"pinyin\": [\"nan\", \"chuan\"] }, { \"fullname\": \"璧山区\", \"id\": \"500120\", \"location\": { \"lat\": 29.59202, \"lng\": 106.22742 }, \"name\": \"璧山\", \"pinyin\": [\"bi\", \"shan\"] }, { \"fullname\": \"铜梁区\", \"id\": \"500151\", \"location\": { \"lat\": 29.84475, \"lng\": 106.05638 }, \"name\": \"铜梁\", \"pinyin\": [\"tong\", \"liang\"] }, { \"fullname\": \"潼南区\", \"id\": \"500152\", \"location\": { \"lat\": 30.19054, \"lng\": 105.83952 }, \"name\": \"潼南\", \"pinyin\": [\"tong\", \"nan\"] }, { \"fullname\": \"荣昌区\", \"id\": \"500153\", \"location\": { \"lat\": 29.41671, \"lng\": 105.61188 }, \"name\": \"荣昌\", \"pinyin\": [\"rong\", \"chang\"] }, { \"fullname\": \"开州区\", \"id\": \"500154\", \"location\": { \"lat\": 31.16098, \"lng\": 108.39311 }, \"name\": \"开州\", \"pinyin\": [\"kai\", \"zhou\"] }, { \"fullname\": \"梁平区\", \"id\": \"500155\", \"location\": { \"lat\": 30.67373, \"lng\": 107.80235 }, \"name\": \"梁平\", \"pinyin\": [\"liang\", \"ping\"] }, { \"fullname\": \"武隆区\", \"id\": \"500156\", \"location\": { \"lat\": 29.32543, \"lng\": 107.75993 }, \"name\": \"武隆\", \"pinyin\": [\"wu\", \"long\"] }, { \"fullname\": \"城口县\", \"id\": \"500229\", \"location\": { \"lat\": 31.94767, \"lng\": 108.66433 }, \"name\": \"城口\", \"pinyin\": [\"cheng\", \"kou\"] }, { \"fullname\": \"丰都县\", \"id\": \"500230\", \"location\": { \"lat\": 29.86352, \"lng\": 107.73085 }, \"name\": \"丰都\", \"pinyin\": [\"feng\", \"du\"] }, { \"fullname\": \"垫江县\", \"id\": \"500231\", \"location\": { \"lat\": 30.3268, \"lng\": 107.33515 }, \"name\": \"垫江\", \"pinyin\": [\"dian\", \"jiang\"] }, { \"fullname\": \"忠县\", \"id\": \"500233\", \"location\": { \"lat\": 30.30026, \"lng\": 108.03767 }, \"name\": \"忠县\", \"pinyin\": [\"zhong\", \"xian\"] }, { \"fullname\": \"云阳县\", \"id\": \"500235\", \"location\": { \"lat\": 30.93063, \"lng\": 108.69698 }, \"name\": \"云阳\", \"pinyin\": [\"yun\", \"yang\"] }, { \"fullname\": \"奉节县\", \"id\": \"500236\", \"location\": { \"lat\": 31.018551, \"lng\": 109.40093 }, \"name\": \"奉节\", \"pinyin\": [\"feng\", \"jie\"] }, { \"fullname\": \"巫山县\", \"id\": \"500237\", \"location\": { \"lat\": 31.07462, \"lng\": 109.8788 }, \"name\": \"巫山\", \"pinyin\": [\"wu\", \"shan\"] }, { \"fullname\": \"巫溪县\", \"id\": \"500238\", \"location\": { \"lat\": 31.3986, \"lng\": 109.57016 }, \"name\": \"巫溪\", \"pinyin\": [\"wu\", \"xi\"] }, { \"fullname\": \"石柱土家族自治县\", \"id\": \"500240\", \"location\": { \"lat\": 29.99968, \"lng\": 108.11415 }, \"name\": \"石柱\", \"pinyin\": [\"shi\", \"zhu\"] }, { \"fullname\": \"秀山土家族苗族自治县\", \"id\": \"500241\", \"location\": { \"lat\": 28.44832, \"lng\": 109.00714 }, \"name\": \"秀山\", \"pinyin\": [\"xiu\", \"shan\"] }, { \"fullname\": \"酉阳土家族苗族自治县\", \"id\": \"500242\", \"location\": { \"lat\": 28.84126, \"lng\": 108.76778 }, \"name\": \"酉阳\", \"pinyin\": [\"you\", \"yang\"] }, { \"fullname\": \"彭水苗族土家族自治县\", \"id\": \"500243\", \"location\": { \"lat\": 29.29376, \"lng\": 108.16555 }, \"name\": \"彭水\", \"pinyin\": [\"peng\", \"shui\"] }, { \"cidx\": [1917, 1936], \"fullname\": \"成都市\", \"id\": \"510100\", \"location\": { \"lat\": 30.5702, \"lng\": 104.06476 }, \"name\": \"成都\", \"pinyin\": [\"cheng\", \"du\"] }, { \"cidx\": [1937, 1942], \"fullname\": \"自贡市\", \"id\": \"510300\", \"location\": { \"lat\": 29.3392, \"lng\": 104.77844 }, \"name\": \"自贡\", \"pinyin\": [\"zi\", \"gong\"] }, { \"cidx\": [1943, 1947], \"fullname\": \"攀枝花市\", \"id\": \"510400\", \"location\": { \"lat\": 26.58228, \"lng\": 101.71872 }, \"name\": \"攀枝花\", \"pinyin\": [\"pan\", \"zhi\", \"hua\"] }, { \"cidx\": [1948, 1954], \"fullname\": \"泸州市\", \"id\": \"510500\", \"location\": { \"lat\": 28.8717, \"lng\": 105.44257 }, \"name\": \"泸州\", \"pinyin\": [\"lu\", \"zhou\"] }, { \"cidx\": [1955, 1960], \"fullname\": \"德阳市\", \"id\": \"510600\", \"location\": { \"lat\": 31.12679, \"lng\": 104.3979 }, \"name\": \"德阳\", \"pinyin\": [\"de\", \"yang\"] }, { \"cidx\": [1961, 1969], \"fullname\": \"绵阳市\", \"id\": \"510700\", \"location\": { \"lat\": 31.46751, \"lng\": 104.6796 }, \"name\": \"绵阳\", \"pinyin\": [\"mian\", \"yang\"] }, { \"cidx\": [1970, 1976], \"fullname\": \"广元市\", \"id\": \"510800\", \"location\": { \"lat\": 32.43549, \"lng\": 105.84357 }, \"name\": \"广元\", \"pinyin\": [\"guang\", \"yuan\"] }, { \"cidx\": [1977, 1981], \"fullname\": \"遂宁市\", \"id\": \"510900\", \"location\": { \"lat\": 30.53286, \"lng\": 105.59273 }, \"name\": \"遂宁\", \"pinyin\": [\"sui\", \"ning\"] }, { \"cidx\": [1982, 1986], \"fullname\": \"内江市\", \"id\": \"511000\", \"location\": { \"lat\": 29.58015, \"lng\": 105.05844 }, \"name\": \"内江\", \"pinyin\": [\"nei\", \"jiang\"] }, { \"cidx\": [1987, 1997], \"fullname\": \"乐山市\", \"id\": \"511100\", \"location\": { \"lat\": 29.55221, \"lng\": 103.76539 }, \"name\": \"乐山\", \"pinyin\": [\"le\", \"shan\"] }, { \"cidx\": [1998, 2006], \"fullname\": \"南充市\", \"id\": \"511300\", \"location\": { \"lat\": 30.83731, \"lng\": 106.11073 }, \"name\": \"南充\", \"pinyin\": [\"nan\", \"chong\"] }, { \"cidx\": [2007, 2012], \"fullname\": \"眉山市\", \"id\": \"511400\", \"location\": { \"lat\": 30.07563, \"lng\": 103.84851 }, \"name\": \"眉山\", \"pinyin\": [\"mei\", \"shan\"] }, { \"cidx\": [2013, 2022], \"fullname\": \"宜宾市\", \"id\": \"511500\", \"location\": { \"lat\": 28.7513, \"lng\": 104.6417 }, \"name\": \"宜宾\", \"pinyin\": [\"yi\", \"bin\"] }, { \"cidx\": [2023, 2028], \"fullname\": \"广安市\", \"id\": \"511600\", \"location\": { \"lat\": 30.45596, \"lng\": 106.63322 }, \"name\": \"广安\", \"pinyin\": [\"guang\", \"an\"] }, { \"cidx\": [2029, 2035], \"fullname\": \"达州市\", \"id\": \"511700\", \"location\": { \"lat\": 31.20864, \"lng\": 107.46791 }, \"name\": \"达州\", \"pinyin\": [\"da\", \"zhou\"] }, { \"cidx\": [2036, 2043], \"fullname\": \"雅安市\", \"id\": \"511800\", \"location\": { \"lat\": 30.01053, \"lng\": 103.0424 }, \"name\": \"雅安\", \"pinyin\": [\"ya\", \"an\"] }, { \"cidx\": [2044, 2048], \"fullname\": \"巴中市\", \"id\": \"511900\", \"location\": { \"lat\": 31.86715, \"lng\": 106.74733 }, \"name\": \"巴中\", \"pinyin\": [\"ba\", \"zhong\"] }, { \"cidx\": [2049, 2051], \"fullname\": \"资阳市\", \"id\": \"512000\", \"location\": { \"lat\": 30.12859, \"lng\": 104.62798 }, \"name\": \"资阳\", \"pinyin\": [\"zi\", \"yang\"] }, { \"cidx\": [2052, 2064], \"fullname\": \"阿坝藏族羌族自治州\", \"id\": \"513200\", \"location\": { \"lat\": 31.8994, \"lng\": 102.22477 }, \"name\": \"阿坝\", \"pinyin\": [\"a\", \"ba\"] }, { \"cidx\": [2065, 2082], \"fullname\": \"甘孜藏族自治州\", \"id\": \"513300\", \"location\": { \"lat\": 30.04932, \"lng\": 101.96254 }, \"name\": \"甘孜\", \"pinyin\": [\"gan\", \"zi\"] }, { \"cidx\": [2083, 2099], \"fullname\": \"凉山彝族自治州\", \"id\": \"513400\", \"location\": { \"lat\": 27.88164, \"lng\": 102.26746 }, \"name\": \"凉山\", \"pinyin\": [\"liang\", \"shan\"] }, { \"cidx\": [2100, 2109], \"fullname\": \"贵阳市\", \"id\": \"520100\", \"location\": { \"lat\": 26.64702, \"lng\": 106.63024 }, \"name\": \"贵阳\", \"pinyin\": [\"gui\", \"yang\"] }, { \"cidx\": [2110, 2113], \"fullname\": \"六盘水市\", \"id\": \"520200\", \"location\": { \"lat\": 26.59336, \"lng\": 104.83023 }, \"name\": \"六盘水\", \"pinyin\": [\"liu\", \"pan\", \"shui\"] }, { \"cidx\": [2114, 2127], \"fullname\": \"遵义市\", \"id\": \"520300\", \"location\": { \"lat\": 27.72545, \"lng\": 106.92723 }, \"name\": \"遵义\", \"pinyin\": [\"zun\", \"yi\"] }, { \"cidx\": [2128, 2133], \"fullname\": \"安顺市\", \"id\": \"520400\", \"location\": { \"lat\": 26.25367, \"lng\": 105.9462 }, \"name\": \"安顺\", \"pinyin\": [\"an\", \"shun\"] }, { \"cidx\": [2134, 2141], \"fullname\": \"毕节市\", \"id\": \"520500\", \"location\": { \"lat\": 27.29847, \"lng\": 105.30504 }, \"name\": \"毕节\", \"pinyin\": [\"bi\", \"jie\"] }, { \"cidx\": [2142, 2151], \"fullname\": \"铜仁市\", \"id\": \"520600\", \"location\": { \"lat\": 27.69066, \"lng\": 109.18099 }, \"name\": \"铜仁\", \"pinyin\": [\"tong\", \"ren\"] }, { \"cidx\": [2152, 2159], \"fullname\": \"黔西南布依族苗族自治州\", \"id\": \"522300\", \"location\": { \"lat\": 25.08988, \"lng\": 104.90437 }, \"name\": \"黔西南\", \"pinyin\": [\"qian\", \"xi\", \"nan\"] }, { \"cidx\": [2160, 2175], \"fullname\": \"黔东南苗族侗族自治州\", \"id\": \"522600\", \"location\": { \"lat\": 26.58364, \"lng\": 107.98416 }, \"name\": \"黔东南\", \"pinyin\": [\"qian\", \"dong\", \"nan\"] }, { \"cidx\": [2176, 2187], \"fullname\": \"黔南布依族苗族自治州\", \"id\": \"522700\", \"location\": { \"lat\": 26.25427, \"lng\": 107.52226 }, \"name\": \"黔南\", \"pinyin\": [\"qian\", \"nan\"] }, { \"cidx\": [2188, 2201], \"fullname\": \"昆明市\", \"id\": \"530100\", \"location\": { \"lat\": 24.87966, \"lng\": 102.83322 }, \"name\": \"昆明\", \"pinyin\": [\"kun\", \"ming\"] }, { \"cidx\": [2202, 2210], \"fullname\": \"曲靖市\", \"id\": \"530300\", \"location\": { \"lat\": 25.49002, \"lng\": 103.79625 }, \"name\": \"曲靖\", \"pinyin\": [\"qu\", \"jing\"] }, { \"cidx\": [2211, 2219], \"fullname\": \"玉溪市\", \"id\": \"530400\", \"location\": { \"lat\": 24.3518, \"lng\": 102.54714 }, \"name\": \"玉溪\", \"pinyin\": [\"yu\", \"xi\"] }, { \"cidx\": [2220, 2224], \"fullname\": \"保山市\", \"id\": \"530500\", \"location\": { \"lat\": 25.11205, \"lng\": 99.16181 }, \"name\": \"保山\", \"pinyin\": [\"bao\", \"shan\"] }, { \"cidx\": [2225, 2235], \"fullname\": \"昭通市\", \"id\": \"530600\", \"location\": { \"lat\": 27.33817, \"lng\": 103.7168 }, \"name\": \"昭通\", \"pinyin\": [\"zhao\", \"tong\"] }, { \"cidx\": [2236, 2240], \"fullname\": \"丽江市\", \"id\": \"530700\", \"location\": { \"lat\": 26.85648, \"lng\": 100.2271 }, \"name\": \"丽江\", \"pinyin\": [\"li\", \"jiang\"] }, { \"cidx\": [2241, 2250], \"fullname\": \"普洱市\", \"id\": \"530800\", \"location\": { \"lat\": 22.82521, \"lng\": 100.96624 }, \"name\": \"普洱\", \"pinyin\": [\"pu\", \"er\"] }, { \"cidx\": [2251, 2258], \"fullname\": \"临沧市\", \"id\": \"530900\", \"location\": { \"lat\": 23.88426, \"lng\": 100.08884 }, \"name\": \"临沧\", \"pinyin\": [\"lin\", \"cang\"] }, { \"cidx\": [2259, 2268], \"fullname\": \"楚雄彝族自治州\", \"id\": \"532300\", \"location\": { \"lat\": 25.04495, \"lng\": 101.52767 }, \"name\": \"楚雄\", \"pinyin\": [\"chu\", \"xiong\"] }, { \"cidx\": [2269, 2281], \"fullname\": \"红河哈尼族彝族自治州\", \"id\": \"532500\", \"location\": { \"lat\": 23.36422, \"lng\": 103.3756 }, \"name\": \"红河\", \"pinyin\": [\"hong\", \"he\"] }, { \"cidx\": [2282, 2289], \"fullname\": \"文山壮族苗族自治州\", \"id\": \"532600\", \"location\": { \"lat\": 23.39849, \"lng\": 104.21504 }, \"name\": \"文山\", \"pinyin\": [\"wen\", \"shan\"] }, { \"cidx\": [2290, 2292], \"fullname\": \"西双版纳傣族自治州\", \"id\": \"532800\", \"location\": { \"lat\": 22.00749, \"lng\": 100.79739 }, \"name\": \"西双版纳\", \"pinyin\": [\"xi\", \"shuang\", \"ban\", \"na\"] }, { \"cidx\": [2293, 2304], \"fullname\": \"大理白族自治州\", \"id\": \"532900\", \"location\": { \"lat\": 25.60648, \"lng\": 100.26764 }, \"name\": \"大理\", \"pinyin\": [\"da\", \"li\"] }, { \"cidx\": [2305, 2309], \"fullname\": \"德宏傣族景颇族自治州\", \"id\": \"533100\", \"location\": { \"lat\": 24.43232, \"lng\": 98.58486 }, \"name\": \"德宏\", \"pinyin\": [\"de\", \"hong\"] }, { \"cidx\": [2310, 2313], \"fullname\": \"怒江傈僳族自治州\", \"id\": \"533300\", \"location\": { \"lat\": 25.81763, \"lng\": 98.8567 }, \"name\": \"怒江\", \"pinyin\": [\"nu\", \"jiang\"] }, { \"cidx\": [2314, 2316], \"fullname\": \"迪庆藏族自治州\", \"id\": \"533400\", \"location\": { \"lat\": 27.81908, \"lng\": 99.70305 }, \"name\": \"迪庆\", \"pinyin\": [\"di\", \"qing\"] }, { \"cidx\": [2317, 2324], \"fullname\": \"拉萨市\", \"id\": \"540100\", \"location\": { \"lat\": 29.64415, \"lng\": 91.1145 }, \"name\": \"拉萨\", \"pinyin\": [\"la\", \"sa\"] }, { \"cidx\": [2325, 2342], \"fullname\": \"日喀则市\", \"id\": \"540200\", \"location\": { \"lat\": 29.26705, \"lng\": 88.88116 }, \"name\": \"日喀则\", \"pinyin\": [\"ri\", \"ka\", \"ze\"] }, { \"cidx\": [2343, 2353], \"fullname\": \"昌都市\", \"id\": \"540300\", \"location\": { \"lat\": 31.14073, \"lng\": 97.17225 }, \"name\": \"昌都\", \"pinyin\": [\"chang\", \"du\"] }, { \"cidx\": [2354, 2360], \"fullname\": \"林芝市\", \"id\": \"540400\", \"location\": { \"lat\": 29.64895, \"lng\": 94.36155 }, \"name\": \"林芝\", \"pinyin\": [\"lin\", \"zhi\"] }, { \"cidx\": [2361, 2372], \"fullname\": \"山南市\", \"id\": \"540500\", \"location\": { \"lat\": 29.23705, \"lng\": 91.77313 }, \"name\": \"山南\", \"pinyin\": [\"shan\", \"nan\"] }, { \"cidx\": [2373, 2383], \"fullname\": \"那曲市\", \"id\": \"540600\", \"location\": { \"lat\": 31.47614, \"lng\": 92.05136 }, \"name\": \"那曲\", \"pinyin\": [\"na\", \"qu\"] }, { \"cidx\": [2384, 2390], \"fullname\": \"阿里地区\", \"id\": \"542500\", \"location\": { \"lat\": 30.40051, \"lng\": 81.1454 }, \"name\": \"阿里\", \"pinyin\": [\"a\", \"li\"] }, { \"cidx\": [2391, 2403], \"fullname\": \"西安市\", \"id\": \"610100\", \"location\": { \"lat\": 34.34127, \"lng\": 108.93984 }, \"name\": \"西安\", \"pinyin\": [\"xi\", \"an\"] }, { \"cidx\": [2404, 2407], \"fullname\": \"铜川市\", \"id\": \"610200\", \"location\": { \"lat\": 34.89673, \"lng\": 108.94515 }, \"name\": \"铜川\", \"pinyin\": [\"tong\", \"chuan\"] }, { \"cidx\": [2408, 2419], \"fullname\": \"宝鸡市\", \"id\": \"610300\", \"location\": { \"lat\": 34.36194, \"lng\": 107.23732 }, \"name\": \"宝鸡\", \"pinyin\": [\"bao\", \"ji\"] }, { \"cidx\": [2420, 2433], \"fullname\": \"咸阳市\", \"id\": \"610400\", \"location\": { \"lat\": 34.32932, \"lng\": 108.70929 }, \"name\": \"咸阳\", \"pinyin\": [\"xian\", \"yang\"] }, { \"cidx\": [2434, 2444], \"fullname\": \"渭南市\", \"id\": \"610500\", \"location\": { \"lat\": 34.49997, \"lng\": 109.51015 }, \"name\": \"渭南\", \"pinyin\": [\"wei\", \"nan\"] }, { \"cidx\": [2445, 2457], \"fullname\": \"延安市\", \"id\": \"610600\", \"location\": { \"lat\": 36.58529, \"lng\": 109.48978 }, \"name\": \"延安\", \"pinyin\": [\"yan\", \"an\"] }, { \"cidx\": [2458, 2468], \"fullname\": \"汉中市\", \"id\": \"610700\", \"location\": { \"lat\": 33.06761, \"lng\": 107.02377 }, \"name\": \"汉中\", \"pinyin\": [\"han\", \"zhong\"] }, { \"cidx\": [2469, 2480], \"fullname\": \"榆林市\", \"id\": \"610800\", \"location\": { \"lat\": 38.2852, \"lng\": 109.73458 }, \"name\": \"榆林\", \"pinyin\": [\"yu\", \"lin\"] }, { \"cidx\": [2481, 2490], \"fullname\": \"安康市\", \"id\": \"610900\", \"location\": { \"lat\": 32.68486, \"lng\": 109.02932 }, \"name\": \"安康\", \"pinyin\": [\"an\", \"kang\"] }, { \"cidx\": [2491, 2497], \"fullname\": \"商洛市\", \"id\": \"611000\", \"location\": { \"lat\": 33.87036, \"lng\": 109.94041 }, \"name\": \"商洛\", \"pinyin\": [\"shang\", \"luo\"] }, { \"cidx\": [2498, 2505], \"fullname\": \"兰州市\", \"id\": \"620100\", \"location\": { \"lat\": 36.06138, \"lng\": 103.83417 }, \"name\": \"兰州\", \"pinyin\": [\"lan\", \"zhou\"] }, { \"cidx\": [2506, 2506], \"fullname\": \"嘉峪关市\", \"id\": \"620200\", \"location\": { \"lat\": 39.77194, \"lng\": 98.28971 }, \"name\": \"嘉峪关\", \"pinyin\": [\"jia\", \"yu\", \"guan\"] }, { \"cidx\": [2507, 2508], \"fullname\": \"金昌市\", \"id\": \"620300\", \"location\": { \"lat\": 38.52006, \"lng\": 102.18759 }, \"name\": \"金昌\", \"pinyin\": [\"jin\", \"chang\"] }, { \"cidx\": [2509, 2513], \"fullname\": \"白银市\", \"id\": \"620400\", \"location\": { \"lat\": 36.5447, \"lng\": 104.13773 }, \"name\": \"白银\", \"pinyin\": [\"bai\", \"yin\"] }, { \"cidx\": [2514, 2520], \"fullname\": \"天水市\", \"id\": \"620500\", \"location\": { \"lat\": 34.58085, \"lng\": 105.72486 }, \"name\": \"天水\", \"pinyin\": [\"tian\", \"shui\"] }, { \"cidx\": [2521, 2524], \"fullname\": \"武威市\", \"id\": \"620600\", \"location\": { \"lat\": 37.9282, \"lng\": 102.63797 }, \"name\": \"武威\", \"pinyin\": [\"wu\", \"wei\"] }, { \"cidx\": [2525, 2530], \"fullname\": \"张掖市\", \"id\": \"620700\", \"location\": { \"lat\": 38.92592, \"lng\": 100.44981 }, \"name\": \"张掖\", \"pinyin\": [\"zhang\", \"ye\"] }, { \"cidx\": [2531, 2537], \"fullname\": \"平凉市\", \"id\": \"620800\", \"location\": { \"lat\": 35.54303, \"lng\": 106.6653 }, \"name\": \"平凉\", \"pinyin\": [\"ping\", \"liang\"] }, { \"cidx\": [2538, 2544], \"fullname\": \"酒泉市\", \"id\": \"620900\", \"location\": { \"lat\": 39.73255, \"lng\": 98.49394 }, \"name\": \"酒泉\", \"pinyin\": [\"jiu\", \"quan\"] }, { \"cidx\": [2545, 2552], \"fullname\": \"庆阳市\", \"id\": \"621000\", \"location\": { \"lat\": 35.70978, \"lng\": 107.64292 }, \"name\": \"庆阳\", \"pinyin\": [\"qing\", \"yang\"] }, { \"cidx\": [2553, 2559], \"fullname\": \"定西市\", \"id\": \"621100\", \"location\": { \"lat\": 35.58113, \"lng\": 104.62524 }, \"name\": \"定西\", \"pinyin\": [\"ding\", \"xi\"] }, { \"cidx\": [2560, 2568], \"fullname\": \"陇南市\", \"id\": \"621200\", \"location\": { \"lat\": 33.401, \"lng\": 104.92166 }, \"name\": \"陇南\", \"pinyin\": [\"long\", \"nan\"] }, { \"cidx\": [2569, 2576], \"fullname\": \"临夏回族自治州\", \"id\": \"622900\", \"location\": { \"lat\": 35.60122, \"lng\": 103.21091 }, \"name\": \"临夏\", \"pinyin\": [\"lin\", \"xia\"] }, { \"cidx\": [2577, 2584], \"fullname\": \"甘南藏族自治州\", \"id\": \"623000\", \"location\": { \"lat\": 34.98327, \"lng\": 102.91102 }, \"name\": \"甘南\", \"pinyin\": [\"gan\", \"nan\"] }, { \"cidx\": [2585, 2591], \"fullname\": \"西宁市\", \"id\": \"630100\", \"location\": { \"lat\": 36.61729, \"lng\": 101.77782 }, \"name\": \"西宁\", \"pinyin\": [\"xi\", \"ning\"] }, { \"cidx\": [2592, 2597], \"fullname\": \"海东市\", \"id\": \"630200\", \"location\": { \"lat\": 36.48209, \"lng\": 102.40173 }, \"name\": \"海东\", \"pinyin\": [\"hai\", \"dong\"] }, { \"cidx\": [2598, 2601], \"fullname\": \"海北藏族自治州\", \"id\": \"632200\", \"location\": { \"lat\": 36.95454, \"lng\": 100.90096 }, \"name\": \"海北\", \"pinyin\": [\"hai\", \"bei\"] }, { \"cidx\": [2602, 2605], \"fullname\": \"黄南藏族自治州\", \"id\": \"632300\", \"location\": { \"lat\": 35.51991, \"lng\": 102.01507 }, \"name\": \"黄南\", \"pinyin\": [\"huang\", \"nan\"] }, { \"cidx\": [2606, 2610], \"fullname\": \"海南藏族自治州\", \"id\": \"632500\", \"location\": { \"lat\": 36.28663, \"lng\": 100.62037 }, \"name\": \"海南\", \"pinyin\": [\"hai\", \"nan\"] }, { \"cidx\": [2611, 2616], \"fullname\": \"果洛藏族自治州\", \"id\": \"632600\", \"location\": { \"lat\": 34.47141, \"lng\": 100.24475 }, \"name\": \"果洛\", \"pinyin\": [\"guo\", \"luo\"] }, { \"cidx\": [2617, 2622], \"fullname\": \"玉树藏族自治州\", \"id\": \"632700\", \"location\": { \"lat\": 33.00528, \"lng\": 97.0065 }, \"name\": \"玉树\", \"pinyin\": [\"yu\", \"shu\"] }, { \"cidx\": [2623, 2629], \"fullname\": \"海西蒙古族藏族自治州\", \"id\": \"632800\", \"location\": { \"lat\": 37.3771, \"lng\": 97.37122 }, \"name\": \"海西\", \"pinyin\": [\"hai\", \"xi\"] }, { \"cidx\": [2630, 2635], \"fullname\": \"银川市\", \"id\": \"640100\", \"location\": { \"lat\": 38.48644, \"lng\": 106.23248 }, \"name\": \"银川\", \"pinyin\": [\"yin\", \"chuan\"] }, { \"cidx\": [2636, 2638], \"fullname\": \"石嘴山市\", \"id\": \"640200\", \"location\": { \"lat\": 38.9841, \"lng\": 106.38418 }, \"name\": \"石嘴山\", \"pinyin\": [\"shi\", \"zui\", \"shan\"] }, { \"cidx\": [2639, 2643], \"fullname\": \"吴忠市\", \"id\": \"640300\", \"location\": { \"lat\": 37.99755, \"lng\": 106.19879 }, \"name\": \"吴忠\", \"pinyin\": [\"wu\", \"zhong\"] }, { \"cidx\": [2644, 2648], \"fullname\": \"固原市\", \"id\": \"640400\", \"location\": { \"lat\": 36.0158, \"lng\": 106.24259 }, \"name\": \"固原\", \"pinyin\": [\"gu\", \"yuan\"] }, { \"cidx\": [2649, 2651], \"fullname\": \"中卫市\", \"id\": \"640500\", \"location\": { \"lat\": 37.50026, \"lng\": 105.19676 }, \"name\": \"中卫\", \"pinyin\": [\"zhong\", \"wei\"] }, { \"cidx\": [2652, 2659], \"fullname\": \"乌鲁木齐市\", \"id\": \"650100\", \"location\": { \"lat\": 43.82663, \"lng\": 87.61688 }, \"name\": \"乌鲁木齐\", \"pinyin\": [\"wu\", \"lu\", \"mu\", \"qi\"] }, { \"cidx\": [2660, 2663], \"fullname\": \"克拉玛依市\", \"id\": \"650200\", \"location\": { \"lat\": 45.57999, \"lng\": 84.88927 }, \"name\": \"克拉玛依\", \"pinyin\": [\"ke\", \"la\", \"ma\", \"yi\"] }, { \"cidx\": [2664, 2666], \"fullname\": \"吐鲁番市\", \"id\": \"650400\", \"location\": { \"lat\": 42.9513, \"lng\": 89.18954 }, \"name\": \"吐鲁番\", \"pinyin\": [\"tu\", \"lu\", \"fan\"] }, { \"cidx\": [2667, 2669], \"fullname\": \"哈密市\", \"id\": \"650500\", \"location\": { \"lat\": 42.81855, \"lng\": 93.51538 }, \"name\": \"哈密\", \"pinyin\": [\"ha\", \"mi\"] }, { \"cidx\": [2670, 2676], \"fullname\": \"昌吉回族自治州\", \"id\": \"652300\", \"location\": { \"lat\": 44.01117, \"lng\": 87.30822 }, \"name\": \"昌吉\", \"pinyin\": [\"chang\", \"ji\"] }, { \"cidx\": [2677, 2680], \"fullname\": \"博尔塔拉蒙古自治州\", \"id\": \"652700\", \"location\": { \"lat\": 44.90597, \"lng\": 82.06665 }, \"name\": \"博州\", \"pinyin\": [\"bo\", \"zhou\"] }, { \"cidx\": [2681, 2689], \"fullname\": \"巴音郭楞蒙古自治州\", \"id\": \"652800\", \"location\": { \"lat\": 41.76404, \"lng\": 86.14517 }, \"name\": \"巴州\", \"pinyin\": [\"ba\", \"zhou\"] }, { \"cidx\": [2690, 2698], \"fullname\": \"阿克苏地区\", \"id\": \"652900\", \"location\": { \"lat\": 41.16842, \"lng\": 80.26008 }, \"name\": \"阿克苏\", \"pinyin\": [\"a\", \"ke\", \"su\"] }, { \"cidx\": [2699, 2702], \"fullname\": \"克孜勒苏柯尔克孜自治州\", \"id\": \"653000\", \"location\": { \"lat\": 39.7153, \"lng\": 76.16661 }, \"name\": \"克州\", \"pinyin\": [\"ke\", \"zhou\"] }, { \"cidx\": [2703, 2714], \"fullname\": \"喀什地区\", \"id\": \"653100\", \"location\": { \"lat\": 39.47042, \"lng\": 75.98976 }, \"name\": \"喀什\", \"pinyin\": [\"ka\", \"shi\"] }, { \"cidx\": [2715, 2722], \"fullname\": \"和田地区\", \"id\": \"653200\", \"location\": { \"lat\": 37.11431, \"lng\": 79.92247 }, \"name\": \"和田\", \"pinyin\": [\"he\", \"tian\"] }, { \"cidx\": [2723, 2733], \"fullname\": \"伊犁哈萨克自治州\", \"id\": \"654000\", \"location\": { \"lat\": 43.91689, \"lng\": 81.32416 }, \"name\": \"伊犁\", \"pinyin\": [\"yi\", \"li\"] }, { \"cidx\": [2734, 2740], \"fullname\": \"塔城地区\", \"id\": \"654200\", \"location\": { \"lat\": 46.74532, \"lng\": 82.98046 }, \"name\": \"塔城\", \"pinyin\": [\"ta\", \"cheng\"] }, { \"cidx\": [2741, 2747], \"fullname\": \"阿勒泰地区\", \"id\": \"654300\", \"location\": { \"lat\": 47.84564, \"lng\": 88.14023 }, \"name\": \"阿勒泰\", \"pinyin\": [\"a\", \"le\", \"tai\"] }, { \"fullname\": \"石河子市\", \"id\": \"659001\", \"location\": { \"lat\": 44.30653, \"lng\": 86.07893 }, \"name\": \"石河子\", \"pinyin\": [\"shi\", \"he\", \"zi\"] }, { \"fullname\": \"阿拉尔市\", \"id\": \"659002\", \"location\": { \"lat\": 40.54798, \"lng\": 81.28067 }, \"name\": \"阿拉尔\", \"pinyin\": [\"a\", \"la\", \"er\"] }, { \"fullname\": \"图木舒克市\", \"id\": \"659003\", \"location\": { \"lat\": 39.86495, \"lng\": 79.06902 }, \"name\": \"图木舒克\", \"pinyin\": [\"tu\", \"mu\", \"shu\", \"ke\"] }, { \"fullname\": \"五家渠市\", \"id\": \"659004\", \"location\": { \"lat\": 44.16799, \"lng\": 87.54017 }, \"name\": \"五家渠\", \"pinyin\": [\"wu\", \"jia\", \"qu\"] }, { \"fullname\": \"北屯市\", \"id\": \"659005\", \"location\": { \"lat\": 47.36327, \"lng\": 87.80014 }, \"name\": \"北屯\", \"pinyin\": [\"bei\", \"tun\"] }, { \"fullname\": \"铁门关市\", \"id\": \"659006\", \"location\": { \"lat\": 41.86868, \"lng\": 85.67583 }, \"name\": \"铁门关\", \"pinyin\": [\"tie\", \"men\", \"guan\"] }, { \"fullname\": \"双河市\", \"id\": \"659007\", \"location\": { \"lat\": 44.84418, \"lng\": 82.35501 }, \"name\": \"双河\", \"pinyin\": [\"shuang\", \"he\"] }, { \"fullname\": \"可克达拉市\", \"id\": \"659008\", \"location\": { \"lat\": 43.94799, \"lng\": 81.04476 }, \"name\": \"可克达拉\", \"pinyin\": [\"ke\", \"ke\", \"da\", \"la\"] }, { \"fullname\": \"昆玉市\", \"id\": \"659009\", \"location\": { \"lat\": 37.20948, \"lng\": 79.29133 }, \"name\": \"昆玉\", \"pinyin\": [\"kun\", \"yu\"] }, { \"cidx\": [2748, 2759], \"fullname\": \"台北市\", \"id\": \"710100\", \"location\": { \"lat\": 25.030724, \"lng\": 121.520076 }, \"name\": \"台北\", \"pinyin\": [\"tai\", \"bei\"] }, { \"cidx\": [2760, 2797], \"fullname\": \"高雄市\", \"id\": \"710200\", \"location\": { \"lat\": 22.630576, \"lng\": 120.306839 }, \"name\": \"高雄\", \"pinyin\": [\"gao\", \"xiong\"] }, { \"cidx\": [2798, 2834], \"fullname\": \"台南市\", \"id\": \"710300\", \"location\": { \"lat\": 22.998601, \"lng\": 120.187817 }, \"name\": \"台南\", \"pinyin\": [\"tai\", \"nan\"] }, { \"cidx\": [2835, 2863], \"fullname\": \"台中市\", \"id\": \"710400\", \"location\": { \"lat\": 24.143171, \"lng\": 120.679882 }, \"name\": \"台中\", \"pinyin\": [\"tai\", \"zhong\"] }, { \"cidx\": [2864, 2876], \"fullname\": \"南投县\", \"id\": \"710600\", \"location\": { \"lat\": 23.919619, \"lng\": 120.670008 }, \"name\": \"南投\", \"pinyin\": [\"nan\", \"tou\"] }, { \"cidx\": [2877, 2883], \"fullname\": \"基隆市\", \"id\": \"710700\", \"location\": { \"lat\": 25.122105, \"lng\": 121.741526 }, \"name\": \"基隆\", \"pinyin\": [\"ji\", \"long\"] }, { \"cidx\": [2884, 2886], \"fullname\": \"新竹市\", \"id\": \"710800\", \"location\": { \"lat\": 24.784924, \"lng\": 120.990745 }, \"name\": \"新竹\", \"pinyin\": [\"xin\", \"zhu\"] }, { \"cidx\": [2887, 2888], \"fullname\": \"嘉义市\", \"id\": \"710900\", \"location\": { \"lat\": 23.485079, \"lng\": 120.472462 }, \"name\": \"嘉义\", \"pinyin\": [\"jia\", \"yi\"] }, { \"cidx\": [2889, 2917], \"fullname\": \"新北市\", \"id\": \"711100\", \"location\": { \"lat\": 25.1853, \"lng\": 121.663675 }, \"name\": \"新北\", \"pinyin\": [\"xin\", \"bei\"] }, { \"cidx\": [2918, 2929], \"fullname\": \"宜兰县\", \"id\": \"711200\", \"location\": { \"lat\": 24.759707, \"lng\": 121.754442 }, \"name\": \"宜兰\", \"pinyin\": [\"yi\", \"lan\"] }, { \"cidx\": [2930, 2942], \"fullname\": \"新竹县\", \"id\": \"711300\", \"location\": { \"lat\": 24.839233, \"lng\": 121.002012 }, \"name\": \"新竹\", \"pinyin\": [\"xin\", \"zhu\"] }, { \"cidx\": [2943, 2955], \"fullname\": \"桃园市\", \"id\": \"711400\", \"location\": { \"lat\": 24.982757, \"lng\": 121.213608 }, \"name\": \"桃园\", \"pinyin\": [\"tao\", \"yuan\"] }, { \"cidx\": [2956, 2973], \"fullname\": \"苗栗县\", \"id\": \"711500\", \"location\": { \"lat\": 24.696762, \"lng\": 120.884337 }, \"name\": \"苗栗\", \"pinyin\": [\"miao\", \"li\"] }, { \"cidx\": [2974, 2999], \"fullname\": \"彰化县\", \"id\": \"711700\", \"location\": { \"lat\": 24.068523, \"lng\": 120.557479 }, \"name\": \"彰化\", \"pinyin\": [\"zhang\", \"hua\"] }, { \"cidx\": [3000, 3017], \"fullname\": \"嘉义县\", \"id\": \"711900\", \"location\": { \"lat\": 23.434473, \"lng\": 120.624255 }, \"name\": \"嘉义\", \"pinyin\": [\"jia\", \"yi\"] }, { \"cidx\": [3018, 3037], \"fullname\": \"云林县\", \"id\": \"712100\", \"location\": { \"lat\": 23.664943, \"lng\": 120.480738 }, \"name\": \"云林\", \"pinyin\": [\"yun\", \"lin\"] }, { \"cidx\": [3038, 3070], \"fullname\": \"屏东县\", \"id\": \"712400\", \"location\": { \"lat\": 22.666716, \"lng\": 120.492005 }, \"name\": \"屏东\", \"pinyin\": [\"ping\", \"dong\"] }, { \"cidx\": [3071, 3086], \"fullname\": \"台东县\", \"id\": \"712500\", \"location\": { \"lat\": 22.764364, \"lng\": 121.113207 }, \"name\": \"台东\", \"pinyin\": [\"tai\", \"dong\"] }, { \"cidx\": [3087, 3099], \"fullname\": \"花莲县\", \"id\": \"712600\", \"location\": { \"lat\": 24.000674, \"lng\": 121.59729 }, \"name\": \"花莲\", \"pinyin\": [\"hua\", \"lian\"] }, { \"cidx\": [3100, 3105], \"fullname\": \"澎湖县\", \"id\": \"712700\", \"location\": { \"lat\": 23.552351, \"lng\": 119.58457 }, \"name\": \"澎湖\", \"pinyin\": [\"peng\", \"hu\"] }, { \"fullname\": \"中西区\", \"id\": \"810101\", \"location\": { \"lat\": 22.27629, \"lng\": 114.16368 }, \"name\": \"中西区\", \"pinyin\": [\"zhong\", \"xi\", \"qu\"] }, { \"fullname\": \"东区\", \"id\": \"810102\", \"location\": { \"lat\": 22.28137, \"lng\": 114.22914 }, \"name\": \"东区\", \"pinyin\": [\"dong\", \"qu\"] }, { \"fullname\": \"九龙城区\", \"id\": \"810103\", \"location\": { \"lat\": 22.30818, \"lng\": 114.18895 }, \"name\": \"九龙\", \"pinyin\": [\"jiu\", \"long\"] }, { \"fullname\": \"观塘区\", \"id\": \"810104\", \"location\": { \"lat\": 22.31057, \"lng\": 114.2306 }, \"name\": \"观塘区\", \"pinyin\": [\"guan\", \"tang\", \"qu\"] }, { \"fullname\": \"南区\", \"id\": \"810105\", \"location\": { \"lat\": 22.24543, \"lng\": 114.15806 }, \"name\": \"南区\", \"pinyin\": [\"nan\", \"qu\"] }, { \"fullname\": \"深水埗区\", \"id\": \"810106\", \"location\": { \"lat\": 22.32921, \"lng\": 114.16856 }, \"name\": \"深水埗区\", \"pinyin\": [\"shen\", \"shui\", \"bu\", \"qu\"] }, { \"fullname\": \"湾仔区\", \"id\": \"810107\", \"location\": { \"lat\": 22.27469, \"lng\": 114.17778 }, \"name\": \"湾仔区\", \"pinyin\": [\"wan\", \"zi\", \"qu\"] }, { \"fullname\": \"黄大仙区\", \"id\": \"810108\", \"location\": { \"lat\": 22.34003, \"lng\": 114.19584 }, \"name\": \"黄大仙区\", \"pinyin\": [\"huang\", \"da\", \"xian\", \"qu\"] }, { \"fullname\": \"油尖旺区\", \"id\": \"810109\", \"location\": { \"lat\": 22.31898, \"lng\": 114.17738 }, \"name\": \"油尖旺区\", \"pinyin\": [\"you\", \"jian\", \"wang\", \"qu\"] }, { \"fullname\": \"离岛区\", \"id\": \"810110\", \"location\": { \"lat\": 22.2817, \"lng\": 113.94691 }, \"name\": \"离岛区\", \"pinyin\": [\"li\", \"dao\", \"qu\"] }, { \"fullname\": \"葵青区\", \"id\": \"810111\", \"location\": { \"lat\": 22.36055, \"lng\": 114.13654 }, \"name\": \"葵青区\", \"pinyin\": [\"kui\", \"qing\", \"qu\"] }, { \"fullname\": \"北区\", \"id\": \"810112\", \"location\": { \"lat\": 22.49181, \"lng\": 114.14312 }, \"name\": \"北区\", \"pinyin\": [\"bei\", \"qu\"] }, { \"fullname\": \"西贡区\", \"id\": \"810113\", \"location\": { \"lat\": 22.37943, \"lng\": 114.27699 }, \"name\": \"西贡区\", \"pinyin\": [\"xi\", \"gong\", \"qu\"] }, { \"fullname\": \"沙田区\", \"id\": \"810114\", \"location\": { \"lat\": 22.3827, \"lng\": 114.19191 }, \"name\": \"沙田区\", \"pinyin\": [\"sha\", \"tian\", \"qu\"] }, { \"fullname\": \"屯门区\", \"id\": \"810115\", \"location\": { \"lat\": 22.38767, \"lng\": 113.98029 }, \"name\": \"屯门区\", \"pinyin\": [\"tun\", \"men\", \"qu\"] }, { \"fullname\": \"大埔区\", \"id\": \"810116\", \"location\": { \"lat\": 22.448, \"lng\": 114.16946 }, \"name\": \"大埔区\", \"pinyin\": [\"da\", \"pu\", \"qu\"] }, { \"fullname\": \"荃湾区\", \"id\": \"810117\", \"location\": { \"lat\": 22.37145, \"lng\": 114.12001 }, \"name\": \"荃湾区\", \"pinyin\": [\"quan\", \"wan\", \"qu\"] }, { \"fullname\": \"元朗区\", \"id\": \"810118\", \"location\": { \"lat\": 22.44243, \"lng\": 114.03181 }, \"name\": \"元朗区\", \"pinyin\": [\"yuan\", \"lang\", \"qu\"] }, { \"fullname\": \"澳门半岛\", \"id\": \"820101\", \"location\": { \"lat\": 22.18684, \"lng\": 113.54294 }, \"name\": \"澳门半岛\", \"pinyin\": [\"ao\", \"men\", \"ban\", \"dao\"] }, { \"fullname\": \"凼仔\", \"id\": \"820102\", \"location\": { \"lat\": 22.15473, \"lng\": 113.55929 }, \"name\": \"凼仔\", \"pinyin\": [\"dang\", \"zi\"] }, { \"fullname\": \"路凼城\", \"id\": \"820103\", \"location\": { \"lat\": 22.13867, \"lng\": 113.56828 }, \"name\": \"路凼城\", \"pinyin\": [\"lu\", \"dang\", \"cheng\"] }, { \"fullname\": \"路环\", \"id\": \"820104\", \"location\": { \"lat\": 22.11501, \"lng\": 113.55724 }, \"name\": \"路环\", \"pinyin\": [\"lu\", \"huan\"] }]"
  },
  {
    "path": "examples/address-book/pages/index/index.js",
    "content": "import {cityData} from './data'\n\nComponent({\n  data: {\n    list: [],\n  },\n  lifetimes: {\n    attached() {\n      const cities = cityData\n      // 按拼音排序\n      cities.sort((c1, c2) => {\n        const pinyin1 = c1.pinyin.join('')\n        const pinyin2 = c2.pinyin.join('')\n        return pinyin1.localeCompare(pinyin2)\n      })\n      // 添加首字母\n      const map = new Map()\n      for (const city of cities) {\n        const alpha = city.pinyin[0].charAt(0).toUpperCase()\n        if (!map.has(alpha)) map.set(alpha, [])\n        map.get(alpha).push({ name: city.fullname })\n      }\n\n      const keys = []\n      for (const key of map.keys()) {\n        keys.push(key)\n      }\n      keys.sort()\n\n      const list = []\n      for (const key of keys) {\n        list.push({\n          alpha: key,\n          subItems: map.get(key)\n        })\n      }\n\n      console.log('address-book list:', list)\n      this.setData({ list })\n    },\n  },\n})\n"
  },
  {
    "path": "examples/address-book/pages/index/index.json",
    "content": "{\n  \"usingComponents\": {\n    \"navigation-bar\": \"../../components/navigation-bar\",\n    \"address-book\": \"../../components/address-book\"\n  },\n  \"disableScroll\": true,\n  \"navigationStyle\": \"custom\"\n}"
  },
  {
    "path": "examples/address-book/pages/index/index.wxml",
    "content": "<navigation-bar title=\"通讯录\" back=\"{{true}}\"></navigation-bar>\n<view class=\"page-container\">\n  <address-book list=\"{{list}}\">\n    <view class=\"page\">\n      <view class=\"page__hd\">\n        <view class=\"page__title\">Address Book</view>\n        <view class=\"page__desc\">类通讯录列表</view>\n      </view>\n      <view class=\"page__bd\">\n      </view>\n    </view>\n  </address-book>\n</view>\n"
  },
  {
    "path": "examples/address-book/pages/index/index.wxss",
    "content": "page {\n  display: flex;\n  flex-direction: column;\n  align-items: flex-end;\n  height: 100vh;\n  background-color: #f7f7f7;\n}\n\n.page-container {\n  width: 100vw;\n  flex: 1;\n  overflow: hidden;\n}\n\n.page {\n  color: rgba(0, 0, 0, .9);\n  font-size: 16px;\n  font-family: -apple-system-font, Helvetica Neue, Helvetica, sans-serif;\n}\n\n.page__hd {\n  padding: 40px\n}\n\n.page__bd {\n  padding-bottom: 40px\n}\n\n.page__title {\n  text-align: left;\n  font-size: 20px;\n  font-weight: 400\n}\n\n.page__desc {\n  margin-top: 5px;\n  color: rgba(0, 0, 0, .5);\n  text-align: left;\n  font-size: 14px\n}\n\n.header {\n  height: 100px;\n}\n\n.cell {\n  height: 50px; \n  justify-content: center;\n  align-items: center;\n}"
  },
  {
    "path": "examples/address-book/project.config.json",
    "content": "{\n  \"appid\": \"wxe5f52902cf4de896\",\n  \"compileType\": \"miniprogram\",\n  \"libVersion\": \"latest\",\n  \"packOptions\": {\n    \"ignore\": [],\n    \"include\": []\n  },\n  \"setting\": {\n    \"coverView\": true,\n    \"es6\": true,\n    \"postcss\": true,\n    \"minified\": true,\n    \"enhance\": true,\n    \"showShadowRootInWxmlPanel\": true,\n    \"packNpmRelationList\": [],\n    \"babelSetting\": {\n      \"ignore\": [],\n      \"disablePlugins\": [],\n      \"outputPath\": \"\"\n    },\n    \"condition\": false,\n    \"skylineRenderEnable\": true,\n    \"compileWorklet\": true\n  },\n  \"condition\": {},\n  \"editorSetting\": {\n    \"tabIndent\": \"insertSpaces\",\n    \"tabSize\": 2\n  },\n  \"projectname\": \"address-book\"\n}"
  },
  {
    "path": "examples/address-book/project.private.config.json",
    "content": "{\n  \"description\": \"项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档：https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html\",\n  \"projectname\": \"address-book\",\n  \"setting\": {\n    \"compileHotReLoad\": false,\n    \"skylineRenderEnable\": true\n  },\n  \"libVersion\": \"latest\"\n}"
  },
  {
    "path": "examples/address-book/sitemap.json",
    "content": "{\n    \"desc\": \"关于本文件的更多信息，请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html\",\n    \"rules\": [{\n    \"action\": \"allow\",\n    \"page\": \"*\"\n    }]\n}"
  },
  {
    "path": "examples/album/app.js",
    "content": "App({})\n"
  },
  {
    "path": "examples/album/app.json",
    "content": "{\n  \"pages\": [\n    \"pages/album/index\",\n    \"pages/preview/index\"\n  ],\n  \"window\": {\n    \"backgroundColor\": \"#ffffff\",\n    \"backgroundTextStyle\": \"dark\",\n    \"navigationBarBackgroundColor\": \"#ffffff\",\n    \"navigationBarTitleText\": \"\",\n    \"navigationBarTextStyle\": \"black\"\n  },\n  \"style\": \"v2\",\n  \"sitemapLocation\": \"sitemap.json\",\n  \"lazyCodeLoading\": \"requiredComponents\",\n  \"componentFramework\": \"glass-easel\",\n  \"renderer\": \"skyline\",\n  \"rendererOptions\": {\n    \"skyline\": {\n      \"defaultDisplayBlock\": true,\n      \"disableABTest\": true,\n      \"sdkVersionBegin\": \"3.0.0\",\n      \"sdkVersionEnd\": \"15.255.255\"\n    }\n  }\n}"
  },
  {
    "path": "examples/album/app.wxss",
    "content": ""
  },
  {
    "path": "examples/album/components/album/album-image/index.js",
    "content": "import EventBus from '../../../utils/event-bus'\nimport { getShared, getRunOnUI } from '../../../utils/worklet'\n\nconst IMAGE_INIT_WIDTH = 0\nconst IMAGE_INIT_HEIGHT = 1\nconst IMAGE_WIDTH = 2\nconst IMAGE_HEIGHT = 3\nconst IMAGE_TARGET_WIDTH = 4\nconst IMAGE_TARGET_HEIGHT = 5\nconst IMAGE_RATIO = 6\nconst IN_PREVIEW = 7\n\nlet screenWidth = 0\nlet screenHeight = 0\n\nComponent({\n  properties: {\n    image: {\n      type: Object,\n      value: {},\n    },\n    src: {\n      type: String,\n      value: '',\n    },\n    width: {\n      type: Number,\n      value: 0,\n    },\n    height: {\n      type: Number,\n      value: 0,\n    },\n  },\n\n  observers: {\n    'width, height'() {\n      this.renderImage()\n    },\n  },\n\n  lifetimes: {\n    created() {\n      const shared = getShared(this.renderer)\n\n      this.sharedValues = [\n        shared(0), // IMAGE_INIT_WIDTH\n        shared(0), // IMAGE_INIT_HEIGHT\n        shared(0), // IMAGE_WIDTH\n        shared(0), // IMAGE_HEIGHT\n        shared(0), // IMAGE_TARGET_WIDTH\n        shared(0), // IMAGE_TARGET_HEIGHT\n        shared(0), // IMAGE_RATIO\n        shared(false), // IN_PREVIEW\n      ]\n    },\n\n    attached() {\n      const pageId = this.getPageId()\n      const uniqueId = `${pageId}-${this.data.image.id}`\n      const sharedValues = this.sharedValues ?? []\n      this.uniqueId = uniqueId\n\n      getRunOnUI(this.renderer)(() => {\n        'worklet'\n        if (!globalThis.temp[`${uniqueId}CustomRouteBack`]) {\n          globalThis.temp[`${uniqueId}CustomRouteBack`] = args => {\n            if (!sharedValues[IN_PREVIEW].value) return\n\n            // 在预览页拖拽返回时会有 scale 效果，所以需要矫正第 0 帧时的宽高，不然会在切 share-element 时突然失去 scale 效果\n            const targetImageWidth = sharedValues[IMAGE_TARGET_WIDTH].value\n            const targetImageHeight = sharedValues[IMAGE_TARGET_HEIGHT].value\n            const scale = args.scale\n            sharedValues[IMAGE_WIDTH].value = targetImageWidth * scale\n            sharedValues[IMAGE_HEIGHT].value = targetImageHeight * scale\n          }\n          globalThis.eventBus.on(`${pageId}CustomRouteBack`, globalThis.temp[`${uniqueId}CustomRouteBack`])\n        }\n      })()\n\n      this.applyAnimatedStyle('.img', () => {\n        'worklet'\n        let width = `${sharedValues[IMAGE_WIDTH].value}px`\n        if (sharedValues[IMAGE_WIDTH].value === 0) {\n          width = ``\n        }\n        let height = `${sharedValues[IMAGE_HEIGHT].value}px`\n        if (sharedValues[IMAGE_HEIGHT].value === 0) {\n          height = ``\n        }\n        return {\n          width,\n          height,\n        }\n      })\n\n      const resetShareValues = () => {\n        sharedValues[IMAGE_WIDTH].value = sharedValues[IMAGE_INIT_WIDTH].value\n        sharedValues[IMAGE_HEIGHT].value = sharedValues[IMAGE_INIT_HEIGHT].value\n        sharedValues[IN_PREVIEW].value = false\n      }\n\n      // 监听预览页图片切换\n      this._onPreviewerChange = image => {\n        if (image.id === this.data.image.id) {\n          // 切到当前图片了\n          sharedValues[IN_PREVIEW].value = true\n        } else {\n          // 切到其他图片了，恢复原样\n          resetShareValues()\n        }\n      }\n      EventBus.on(`${pageId}PreviewerChange`, this._onPreviewerChange)\n\n      this._onPreviewerHide = () => {\n        // 预览页销毁了，恢复原样\n        // 这里可能有返回动画，所以延迟 reset\n        setTimeout(resetShareValues, 500)\n      }\n      EventBus.on(`${pageId}PreviewerDestroy`, this._onPreviewerHide)\n    },\n\n    detached() {\n      const pageId = this.getPageId()\n      const uniqueId = this.uniqueId\n      getRunOnUI(this.renderer)(() => {\n        'worklet'\n        if (globalThis.temp[`${uniqueId}CustomRouteBack`]) {\n          globalThis.eventBus.off(`${pageId}CustomRouteBack`, globalThis.temp[`${uniqueId}CustomRouteBack`])\n          delete globalThis.temp[`${uniqueId}CustomRouteBack`]\n        }\n      })()\n\n      EventBus.off(`${pageId}PreviewerChange`, this._onPreviewerChange)\n      EventBus.off(`${pageId}PreviewerDestroy`, this._onPreviewerHide)\n    },\n  },\n\n  methods: {\n    onFrame(evt) {\n      'worklet'\n      console.log('worklet onFrame', evt)\n      // 进入预览页的动画，会逐帧调用\n      // 在此回调中需要根据当前容器的大小来调整图片的大小，因为图片是自己设置宽高渲染的，不能根据容器宽高自适应\n      const rect = evt.current\n      const sharedValues = this.sharedValues ?? []\n      const cntWidth = rect.width\n      const cntHeight = rect.height\n      const progress = evt.progress // 当前动画进度\n      const imageRatio = sharedValues[IMAGE_RATIO].value\n      const isPop = evt.direction === 1\n\n      let width = cntWidth\n      let height = cntHeight\n      if (imageRatio) {\n        const cntRatio = cntWidth / cntHeight\n\n        if (cntRatio > imageRatio) height = cntWidth / imageRatio\n        else if (cntRatio <= imageRatio) width = cntHeight * imageRatio\n\n        // 获取图片的初始大小和目标大小\n        const initImageWidth = sharedValues[IMAGE_INIT_WIDTH].value\n        const initImageHeight = sharedValues[IMAGE_INIT_HEIGHT].value\n        const targetImageWidth = sharedValues[IMAGE_TARGET_WIDTH].value\n        const targetImageHeight = sharedValues[IMAGE_TARGET_HEIGHT].value\n        if (initImageWidth && initImageHeight && targetImageWidth && targetImageHeight) {\n          if (isPop) {\n            // 退出动画\n            width = targetImageWidth - (targetImageWidth - initImageWidth) * progress\n            height = targetImageHeight - (targetImageHeight - initImageHeight) * progress\n          } else {\n            // 进入动画\n            width = initImageWidth + (targetImageWidth - initImageWidth) * progress\n            height = initImageHeight + (targetImageHeight - initImageHeight) * progress\n          }\n        }\n      }\n\n      sharedValues[IMAGE_WIDTH].value = width\n      sharedValues[IMAGE_HEIGHT].value = height\n    },\n\n    onImageLoad(evt) {\n      const { width, height } = evt.detail\n      const sharedValues = this.sharedValues ?? []\n      const imageRatio = width / height\n\n      this.imageRatio = imageRatio\n      sharedValues[IMAGE_RATIO].value = imageRatio\n      this.renderImage()\n    },\n\n    renderImage() {\n      // 因为目标预览页的 mode 是 aspectFill，为了保证进入预览页的动画过程中不会发生 mode 跳变问题，故此处使用 aspectFill，然后手动裁剪成 aspectFit\n      const sharedValues = this.sharedValues ?? []\n      const { width: cntWidth, height: cntHeight } = this.data\n      const imageRatio = this.imageRatio\n\n      if (!screenWidth || !screenHeight) {\n        const systemInfo = wx.getSystemInfoSync()\n        screenWidth = systemInfo.screenWidth\n        screenHeight = systemInfo.screenHeight\n      }\n\n      if (cntWidth && cntHeight && imageRatio) {\n        let initWidth = cntWidth\n        let initHeight = cntHeight\n        let targetImageWidth = screenWidth\n        let targetImageHeight = screenHeight\n        const cntRatio = cntWidth / cntHeight\n        const targetRatio = screenWidth / screenHeight\n\n        if (cntRatio > imageRatio) {\n          initHeight = cntWidth / imageRatio\n        } else if (cntRatio < imageRatio) {\n          initWidth = cntHeight * imageRatio\n        }\n\n        if (targetRatio > imageRatio) {\n          targetImageWidth = targetImageHeight * imageRatio\n        } else if (targetRatio < imageRatio) {\n          targetImageHeight = targetImageWidth / imageRatio\n        }\n\n        // 在相册页面时的初始大小\n        sharedValues[IMAGE_INIT_WIDTH].value = initWidth\n        sharedValues[IMAGE_INIT_HEIGHT].value = initHeight\n\n        // 当前图片的大小\n        sharedValues[IMAGE_WIDTH].value = initWidth\n        sharedValues[IMAGE_HEIGHT].value = initHeight\n\n        // 动画到预览页时的目标大小\n        sharedValues[IMAGE_TARGET_WIDTH].value = targetImageWidth\n        sharedValues[IMAGE_TARGET_HEIGHT].value = targetImageHeight\n      }\n    },\n  },\n})\n"
  },
  {
    "path": "examples/album/components/album/album-image/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {},\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/album/components/album/album-image/index.wxml",
    "content": "<share-element class=\"share-element\" key=\"{{image.id}}\" rect-tween-type=\"cubic-bezier(0.4, 0, 0.2, 1.0)\" worklet:onframe=\"onFrame\">\n  <view class=\"img-cut-cnt\">\n    <image class=\"img\" src=\"{{src}}\" mode=\"aspectFill\" bindload=\"onImageLoad\"></image>\n  </view>\n</share-element>\n"
  },
  {
    "path": "examples/album/components/album/album-image/index.wxss",
    "content": ".share-element {\n  position: relative;\n  width: 100%;\n  height: 100%;\n}\n\n.img-cut-cnt {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  background-color: transparent;\n  overflow: hidden;\n}\n\n.img {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 100%;\n  height: 100%;\n  transform: translate(-50%, -50%);\n  transform-origin: center;\n  z-index: 5;\n}\n"
  },
  {
    "path": "examples/album/components/album/index.js",
    "content": "import EventBus from '../../utils/event-bus'\nimport { initRoute } from './route'\n\nfunction transformListToLineBlock(list, lineLimit) {\n  const lineList = []\n  for (let i = 0, len = list.length; i < len; i += lineLimit) {\n    const line = { index: Math.floor(i / lineLimit), list: [] }\n    for (let j = 0; j < lineLimit; j++) {\n      const index = i + j\n      const item = list[index]\n      if (item) line.list.push({\n        ...item,\n        index,\n      })\n    }\n    lineList.push(line)\n  }\n  return lineList\n}\n\nComponent({\n  properties: {\n    list: {\n      type: Array,\n      value: [],\n    },\n\n    imageWidth: {\n      type: Number,\n      value: 0,\n    },\n\n    imageMargin: {\n      type: Number,\n      value: 0,\n    },\n\n    lineLimit: {\n      type: Number,\n      value: 3,\n    },\n  },\n\n  data: {\n    showList: [],\n    scrollIntoView: '',\n  },\n\n  observers: {\n    'list, lineLimit'(list, lineLimit) {\n      if (!list.length || !lineLimit) return\n\n      // 调整为行结构，每行自己排版\n      this.setData({\n        showList: transformListToLineBlock(list, lineLimit),\n      })\n    },\n  },\n\n  lifetimes: {\n    created() {\n      EventBus.initWorkletEventBus(this.renderer) // 初始化 ui 线程的 eventBus\n      initRoute() // 初始化自定义路由\n    },\n\n    attached() {\n      // 预览页发生图片切换时，要将对应 image 滚动到到可视范围内\n      const pageId = this.getPageId()\n      let scrollIntoViewTimer = null\n      this._onPreviewerChange = image => {\n        const list = this.data.list\n        const index = list.findIndex(item => item.id === image.id)\n  \n        if (index !== -1) {\n          if (scrollIntoViewTimer) clearTimeout(scrollIntoViewTimer)\n          scrollIntoViewTimer = setTimeout(() => {\n            // 可能处于页面切换动画中，故加个延迟再滚动\n            let lineIndex = Math.floor(index / this.data.lineLimit)\n            this.setData({ scrollIntoView: `line-${lineIndex}` })\n          }, 500)\n        }\n      }\n      EventBus.on(`${pageId}PreviewerChange`, this._onPreviewerChange)\n    },\n\n    detached() {\n      const pageId = this.getPageId()\n      EventBus.off(`${pageId}PreviewerChange`, this._onPreviewerChange)\n    },\n  },\n\n  methods: {\n    onTapImage(evt) {\n      const { index } = evt.currentTarget.dataset || {}\n      const image = this.data.list[index]\n      if (!image) return\n\n      // 跳转到预览页\n      wx.navigateTo({\n        url: `../../pages/preview/index?imageid=${image.id}&sourcepageid=${this.getPageId()}`,\n        routeType: 'fadeToggle',\n      })\n    },\n  },\n})\n"
  },
  {
    "path": "examples/album/components/album/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {\n    \"album-image\": \"./album-image/index\"\n  },\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/album/components/album/index.wxml",
    "content": "  <scroll-view\n    class=\"scroll-list\"\n    style=\"padding-top: {{imageMargin}}px;\"\n    scroll-y\n    type=\"list\"\n    scroll-into-view=\"{{scrollIntoView}}\"\n    scroll-into-view-alignment=\"nearest\"\n  >\n    <view\n      wx:for=\"{{showList}}\"\n      wx:key=\"index\"\n      wx:for-item=\"line\"\n      id=\"line-{{index}}\"\n      class=\"line\"\n      style=\"padding-bottom: {{imageMargin}}px;\"\n    >\n      <view wx:for=\"{{line.list}}\" wx:key=\"id\" style=\"width: {{imageWidth}}px; height: {{imageWidth}}px; margin-left: {{imageMargin}}px;\">\n        <album-image\n          class=\"album-image\"\n          image=\"{{item}}\"\n          src=\"{{item.src}}\"\n          width=\"{{imageWidth}}\"\n          height=\"{{imageWidth}}\"\n          data-index=\"{{item.index}}\"\n          bindtap=\"onTapImage\"\n        ></album-image>\n      </view>\n    </view>\n  </scroll-view>"
  },
  {
    "path": "examples/album/components/album/index.wxss",
    "content": ".scroll-list {\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n}\n\n.line {\n  display: flex;\n  flex-direction: row;\n  justify-content: flex-start;\n}\n\n.album-image {\n  width: 100%;\n  height: 100%;\n}\n"
  },
  {
    "path": "examples/album/components/album/route.js",
    "content": "const fastOutSlowIn = wx.worklet.Easing.bezier(0.4, 0.0, 0.2, 1.0).factory()\n\nexport function initRoute() {\n  wx.router.addRouteBuilder('fadeToggle', ({ primaryAnimation }) => {\n    const handlePrimaryAnimation = () => {\n      'worklet'\n      return {\n        opacity: fastOutSlowIn(primaryAnimation.value),\n      }\n    }\n\n    return {\n      opaque: false,\n      handlePrimaryAnimation,\n      transitionDuration: 300,\n      reverseTransitionDuration: 300,\n      canTransitionTo: false,\n      canTransitionFrom: false,\n    }\n  })\n}\n"
  },
  {
    "path": "examples/album/components/navigation-bar/index.js",
    "content": "Component({\n  options: {\n    multipleSlots: true // 在组件定义时的选项中启用多slot支持\n  },\n  /**\n   * 组件的属性列表\n   */\n  properties: {\n    extClass: {\n      type: String,\n      value: ''\n    },\n    title: {\n      type: String,\n      value: ''\n    },\n    background: {\n      type: String,\n      value: ''\n    },\n    color: {\n      type: String,\n      value: ''\n    },\n    back: {\n      type: Boolean,\n      value: true\n    },\n    loading: {\n      type: Boolean,\n      value: false\n    },\n    animated: {\n      // 显示隐藏的时候opacity动画效果\n      type: Boolean,\n      value: true\n    },\n    show: {\n      // 显示隐藏导航，隐藏的时候navigation-bar的高度占位还在\n      type: Boolean,\n      value: true,\n      observer: '_showChange'\n    },\n    // back为true的时候，返回的页面深度\n    delta: {\n      type: Number,\n      value: 1\n    }\n  },\n  /**\n   * 组件的初始数据\n   */\n  data: {\n    displayStyle: ''\n  },\n  attached() {\n    const rect = wx.getMenuButtonBoundingClientRect()\n    wx.getSystemInfo({\n      success: (res) => {\n        this.setData({\n          statusBarHeight: res.statusBarHeight,\n          innerPaddingRight: `padding-right:${res.windowWidth - rect.left}px`,\n          leftWidth: `width:${res.windowWidth - rect.left}px`,\n          navBarHeight: rect.bottom + rect.top - res.statusBarHeight,\n        })\n      }\n    })\n  },\n  /**\n   * 组件的方法列表\n   */\n  methods: {\n    _showChange(show) {\n      const animated = this.data.animated\n      let displayStyle = ''\n      if (animated) {\n        displayStyle = `opacity: ${show ? '1' : '0'};transition: opacity 0.5s;`\n      } else {\n        displayStyle = `display: ${show ? '' : 'none'}`\n      }\n      this.setData({\n        displayStyle\n      })\n    },\n    back() {\n      const data = this.data\n      if (data.delta) {\n        wx.navigateBack({\n          delta: data.delta\n        })\n      }\n      this.triggerEvent('back', { delta: data.delta }, {})\n    }\n  }\n})\n"
  },
  {
    "path": "examples/album/components/navigation-bar/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {},\n  \"addGlobalClass\": true,\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/album/components/navigation-bar/index.wxml",
    "content": "<view class=\"weui-navigation-bar {{extClass}}\">\n  <view class=\"weui-navigation-bar__inner\" style=\"padding-top: {{statusBarHeight}}px; height: {{navBarHeight}}px; color: {{color}}; background: {{background}}; {{displayStyle}}; {{innerPaddingRight}};\">\n    <view class='weui-navigation-bar__left' style=\"{{leftWidth}}\">\n      <block wx:if=\"{{back}}\">\n        <view class=\"weui-navigation-bar__buttons\">\n          <view bindtap=\"back\" class=\"weui-navigation-bar__btn_goback_wrapper\" hover-class=\"weui-active\" aria-role=\"button\" aria-label=\"返回\">\n            <view class=\"weui-navigation-bar__button weui-navigation-bar__btn_goback\"></view>\n          </view>\n        </view>\n      </block>\n      <block wx:else>\n        <slot name=\"left\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__center'>\n      <view wx:if=\"{{loading}}\" class=\"weui-navigation-bar__loading\" aria-role=\"alert\">\n        <view class=\"weui-loading\" style=\"width:{{size.width}}rpx;height:{{size.height}}rpx;\" aria-role=\"img\" aria-label=\"加载中\"></view>\n      </view>\n      <block wx:if=\"{{title}}\">\n        <text>{{title}}</text>\n      </block>\n      <block wx:else>\n        <slot name=\"center\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__right'>\n      <slot name=\"right\"></slot>\n    </view>\n  </view>\n</view>\n"
  },
  {
    "path": "examples/album/components/navigation-bar/index.wxss",
    "content": ".weui-navigation-bar {\n  overflow: hidden;\n  color: rgba(0, 0, 0, .9);\n  width: 100vw;\n}\n\n.weui-navigation-bar__placeholder {\n  background: #f7f7f7;\n  position: relative;\n}\n\n.weui-navigation-bar__inner, .weui-navigation-bar__inner .weui-navigation-bar__left {\n  display: flex;\n  align-items: center;\n  flex-direction: row;\n}\n\n.weui-navigation-bar__inner {\n  position: relative;\n  padding-right: 95px;\n  width: 100vw;\n  box-sizing: border-box;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left {\n  position: relative;\n  width: 95px;\n  padding-left: 16px;\n  box-sizing: border-box;\n}\n\n.weui-navigation-bar__btn_goback_wrapper {\n  padding: 11px 18px 11px 16px;\n  margin: -11px -18px -11px -16px;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left .weui-navigation-bar__btn_goback {\n  font-size: 12px;\n  width: 12px;\n  height: 24px;\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E\") no-repeat 50% 50%;\n  background-size: cover;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__center {\n  font-size: 17px;\n  text-align: center;\n  position: relative;\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-weight: bold;\n}\n\n@media(prefers-color-scheme: dark) {\n  .weui-navigation-bar {\n    color: hsla(0, 0%, 100%, .8);\n  }\n  .weui-navigation-bar__inner {\n    background-color: #1f1f1f;\n  }\n}\n"
  },
  {
    "path": "examples/album/components/previewer/index.js",
    "content": "import EventBus from '../../utils/event-bus'\nimport { getShared, getTiming, getRunOnUI } from '../../utils/worklet'\n\nconst PreviewerGesture = {\n  Init: 0, // 初始\n  Moving: 1, // 移动图片\n  Toggle: 2, // 切换图片\n  Back: 3, // 退出页面\n}\n\nconst PrimaryAnimationStatus = {\n  DISMISSED: 0,\n  FORWARD: 1,\n  REVERSE: 2,\n  COMPLETED: 3,\n}\n\nconst GestureState = {\n  POSSIBLE: 0,\n  BEGIN: 1,\n  ACTIVE: 2,\n  END: 3,\n  CANCELLED: 4,\n}\n\n// sharedValues\nconst TRANSLATE_X = 0\nconst TRANSLATE_Y = 1\nconst START_Y = 2\nconst OPACITY = 3\nconst SCALE = 4\nconst MIN_SCALE = 5\nconst USER_GESTURE_IN_PROGRESS = 6\nconst GESTURE_STATE = 7\nconst PAGE_ID = 8\nconst TEMP_LAST_SCALE = 9\nconst RENDERER = 10\n\nfunction clamp(value, min, max) {\n  'worklet'\n  return Math.min(max, Math.max(min, value))\n}\n\nfunction recoverTiming(target, renderer, callback) {\n  'worklet'\n  return getTiming(renderer)(target, { duration: 200 }, callback)\n}\n\nfunction calcOpacity(moveY, screenHeight) {\n  'worklet'\n  const opacityRatio = moveY / (screenHeight / 2)\n  return clamp((1 - opacityRatio) ** 3, 0, 1) // 最透明程度为 0\n}\n\nfunction calcScale(moveY, screenHeight) {\n  'worklet'\n  const scaleRange = 0.4\n  const scaleRatio = moveY / (screenHeight / 3 * 2)\n  return clamp(1 - scaleRange * scaleRatio, 0.6, 1) // 最小为 0.6\n}\n\nComponent({\n  properties: {\n    imageId: {\n      type: String,\n      value: '',\n    },\n    sourcePageId: {\n      type: String,\n      value: '',\n    },\n    list: {\n      type: Array,\n      value: [],\n    },\n  },\n\n  lifetimes: {\n    created() {\n      EventBus.initWorkletEventBus(this.renderer) // 初始化 ui 线程的 eventBus\n\n      const shared = getShared(this.renderer)\n\n      this.sharedValues = [\n        shared(0), // TRANSLATE_X\n        shared(0), // TRANSLATE_Y\n        shared(0), // START_Y\n        shared(1), // OPACITY\n        shared(1), // SCALE\n        shared(1), // MIN_SCALE\n        shared(false), // USER_GESTURE_IN_PROGRESS\n        shared(0), // GESTURE_STATE\n        shared(0), // PAGE_ID\n        shared(1), // TEMP_LAST_SCALE\n        shared(this.renderer), // RENDERER\n      ]\n    },\n\n    attached() {\n      const sourcePageId = this.data.sourcePageId\n      const { screenHeight } = wx.getSystemInfoSync()\n      const pageId = this.getPageId()\n      const sharedValues = this.sharedValues ?? []\n\n      sharedValues[PAGE_ID].value = pageId\n\n      this.customRouteContext = wx.router.getRouteContext(this)\n\n      getRunOnUI(this.renderer)(() => {\n        'worklet'\n        // 监听拖拽返回手势\n        if (!globalThis.temp[`${pageId}GestureBack`]) {\n          globalThis.temp[`${pageId}GestureBack`] = args => {\n            if (sharedValues[GESTURE_STATE].value === PreviewerGesture.Moving) return\n            sharedValues[GESTURE_STATE].value = PreviewerGesture.Back\n\n            const { moveX, moveY, offsetY } = args\n\n            // 横向永远跟手走\n            sharedValues[TRANSLATE_X].value += moveX\n\n            // 竖向在手指回到原处再往上时增加阻尼\n            if (sharedValues[TRANSLATE_Y].value < 0) {\n              const fy = 0.52 * ((1 - Math.min(offsetY / screenHeight, 1)) ** 2)\n              const translateY = sharedValues[TRANSLATE_Y].value + Math.ceil(moveY * fy)\n              sharedValues[TRANSLATE_Y].value = translateY\n            } else {\n              sharedValues[TRANSLATE_Y].value += moveY\n            }\n\n            // 拖动时的渐变\n            sharedValues[OPACITY].value = calcOpacity(offsetY, screenHeight)\n\n            // 拖动时的大小变化\n            const scale = calcScale(offsetY, screenHeight)\n            sharedValues[SCALE].value = scale\n            sharedValues[MIN_SCALE].value = Math.min(scale, sharedValues[MIN_SCALE].value)\n          }\n          globalThis.eventBus.on(`${pageId}Back`, globalThis.temp[`${pageId}GestureBack`])\n        }\n\n        // 监听拖拽返回手势结束\n        if (!globalThis.temp[`${pageId}GestureBackEnd`]) {\n          globalThis.temp[`${pageId}GestureBackEnd`] = () => {\n            const moveY = sharedValues[TRANSLATE_Y].value\n            const scale = sharedValues[SCALE].value\n            const minScale = sharedValues[MIN_SCALE].value\n            const { didPop } = this.customRouteContext || {}\n\n            if (moveY > 1 && scale <= (minScale + 0.01)) {\n              // 必须是一直处于缩小行为才退页面，否则恢复\n              globalThis.eventBus.emit(`${sourcePageId}CustomRouteBack`, { scale })\n              didPop()\n            } else {\n              const renderer = sharedValues[RENDERER].value\n              sharedValues[OPACITY].value = recoverTiming(1, renderer)\n              sharedValues[TRANSLATE_X].value = recoverTiming(0, renderer)\n              sharedValues[TRANSLATE_Y].value = recoverTiming(0, renderer)\n              sharedValues[SCALE].value = recoverTiming(1, renderer, () => {\n                'worklet'\n                sharedValues[GESTURE_STATE].value = PreviewerGesture.Init\n              })\n              sharedValues[MIN_SCALE].value = 1\n            }\n          }\n          globalThis.eventBus.on(`${pageId}BackEnd`, globalThis.temp[`${pageId}GestureBackEnd`])\n        }\n\n        // 监听图片切换手势\n        if (!globalThis.temp[`${pageId}GestureToggle`]) {\n          globalThis.temp[`${pageId}GestureToggle`] = () => {\n            if (sharedValues[GESTURE_STATE].value === PreviewerGesture.Moving) return\n            sharedValues[GESTURE_STATE].value = PreviewerGesture.Toggle\n          }\n          globalThis.eventBus.on(`${pageId}Toggle`, globalThis.temp[`${pageId}GestureToggle`])\n        }\n\n        // 监听图片拖动手势\n        if (!globalThis.temp[`${pageId}GestureMoving`]) {\n          globalThis.temp[`${pageId}GestureMoving`] = () => {\n            sharedValues[GESTURE_STATE].value = PreviewerGesture.Moving\n          }\n          globalThis.eventBus.on(`${pageId}Moving`, globalThis.temp[`${pageId}GestureMoving`])\n        }\n      })()\n\n      // 拖拽返回时，被拖动的 share-element，因为放手时的返回动画是以两个页面的 share-element 之间的位置进行移动，故这里移动的是 share-element\n      this.applyAnimatedStyle('#preview-home >>> .need-transform-on-back', () => {\n        'worklet'\n        return {\n          transform: `translate(${sharedValues[TRANSLATE_X].value}px, ${sharedValues[TRANSLATE_Y].value}px) scale(${sharedValues[SCALE].value})`,\n        }\n      })\n\n      const { primaryAnimation, primaryAnimationStatus } = this.customRouteContext || {}\n\n      // 拖拽返回时，previewer 本体要隐藏\n      this.applyAnimatedStyle('#preview-home >>> .need-hide-on-back', () => {\n        'worklet'\n        const status = primaryAnimationStatus.value\n        const isRunningAnimation = status === 1 || status === 2\n\n        return {\n          left: (isRunningAnimation || (sharedValues[GESTURE_STATE].value === PreviewerGesture.Back)) ? '9999px' : '0',\n        }\n      })\n\n      // 图片背景渐变消失\n      this.applyAnimatedStyle('#preview-home >>> .preview-middle-self', () => {\n        'worklet'\n        const status = primaryAnimationStatus.value\n        const opacity = sharedValues[OPACITY].value\n\n        if (!sharedValues[USER_GESTURE_IN_PROGRESS].value) {\n          // 非手势触发，则加速动画\n          const value = primaryAnimation.value\n          let factor = value\n          if (status === PrimaryAnimationStatus.FORWARD) {\n            factor *= 3\n            if (factor > 1) factor = 1\n          } else if (status === PrimaryAnimationStatus.REVERSE) {\n            factor = 1 - ((1 - factor) * 3)\n            if (factor < 0) factor = 0\n          }\n\n          const newOpacity = opacity * factor\n          return { opacity: newOpacity > 1 ? 1 : newOpacity }\n        } else {\n          // 手势触发\n          return { opacity }\n        }\n      })\n    },\n\n    detached() {\n      const pageId = this.getPageId()\n      getRunOnUI(this.renderer)(() => {\n        'worklet'\n        const removeList = ['Back', 'BackEnd', 'Toggle', 'Moving']\n        removeList.forEach(item => {\n          'worklet'\n          const globalKey = `${pageId}Gesture${item}`\n          if (globalThis.temp[globalKey]) {\n            globalThis.eventBus.off(`${pageId}${item}`, globalThis.temp[globalKey])\n            delete globalThis.temp[globalKey]\n          }\n        })\n      })()\n    },\n  },\n\n  methods: {\n    onScale(evt) {\n      'worklet'\n      const sharedValues = this.sharedValues ?? []\n      const pageId = sharedValues[PAGE_ID].value\n\n      if (evt.state === GestureState.BEGIN) {\n        sharedValues[START_Y].value = evt.focalY\n        sharedValues[TEMP_LAST_SCALE].value = 1\n        sharedValues[GESTURE_STATE].value = PreviewerGesture.Init\n        sharedValues[USER_GESTURE_IN_PROGRESS].value = true\n      } else if (evt.state === GestureState.ACTIVE) {\n        const focalX = evt.focalX\n        const focalY = evt.focalY\n        const moveX = evt.focalDeltaX\n        const moveY = evt.focalDeltaY\n        const offsetY = focalY - sharedValues[START_Y].value\n\n        if (evt.pointerCount === 2) {\n          // 双指放缩\n          const pageId = sharedValues[PAGE_ID].value\n          const realScale = evt.scale / sharedValues[TEMP_LAST_SCALE].value\n          sharedValues[TEMP_LAST_SCALE].value = evt.scale\n\n          globalThis.eventBus.emit(`${pageId}Scale`, {\n            scale: realScale,\n            centerX: focalX,\n            centerY: focalY,\n          })\n        } else if (sharedValues[GESTURE_STATE].value === PreviewerGesture.Back) {\n          // 处于拖拽返回手势中\n          globalThis.eventBus.emit(`${pageId}Back`, {\n            moveX,\n            moveY,\n            offsetY,\n          })\n        } else if (sharedValues[GESTURE_STATE].value === PreviewerGesture.Toggle) {\n          // 处于切换图片手势中\n          // ignore\n        } else {\n          // 单指拖动图片\n          globalThis.eventBus.emit(`${pageId}Move`, {\n            moveX,\n            moveY,\n            offsetY,\n            origin: 'move',\n          })\n        }\n      } else if (evt.state === GestureState.END || evt.state === GestureState.CANCELLED) {\n        const velocityX = evt.velocityX\n        const velocityY = evt.velocityY\n        sharedValues[USER_GESTURE_IN_PROGRESS].value = false\n\n        if (sharedValues[GESTURE_STATE].value === PreviewerGesture.Back) {\n          // 拖拽返回手势结束\n          globalThis.eventBus.emit(`${pageId}BackEnd`)\n        } else if (sharedValues[GESTURE_STATE].value === PreviewerGesture.Toggle) {\n          // 切换图片手势结束\n          sharedValues[GESTURE_STATE].value = PreviewerGesture.Init\n        } else {\n          // 其他手势结束\n          globalThis.eventBus.emit(`${pageId}End`, {\n            velocityX,\n            velocityY,\n          })\n          sharedValues[GESTURE_STATE].value = PreviewerGesture.Init\n        }\n      }\n    },\n\n    onBack() {\n      // 进入动画返回状态\n      const sharedValues = this.sharedValues ?? []\n      sharedValues[GESTURE_STATE].value = PreviewerGesture.Back\n    },\n\n    shouldResponseOnMove() {\n      'worklet'\n      return true\n    },\n  },\n})\n"
  },
  {
    "path": "examples/album/components/previewer/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {\n    \"preview-home\": \"./preview-home/index\"\n  },\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/album/components/previewer/index.wxml",
    "content": "<scale-gesture-handler\n  tag=\"scale\"\n  simultaneousHandlers=\"{{['swiper']}}\"\n  worklet:ongesture=\"onScale\"\n  worklet:should-response-on-move=\"shouldResponseOnMove\"\n>\n  <preview-home\n    id=\"preview-home\"\n    class=\"preview-home\"\n    imageId=\"{{imageId}}\"\n    sourcePageId=\"{{sourcePageId}}\"\n    list=\"{{list}}\"\n    bindback=\"onBack\"\n  ></preview-home>\n</scale-gesture-handler>"
  },
  {
    "path": "examples/album/components/previewer/index.wxss",
    "content": ".preview-home {\n  width: 100%;\n  height: 100%;\n  display: block;\n}\n"
  },
  {
    "path": "examples/album/components/previewer/preview-home/index.js",
    "content": "import EventBus from '../../../utils/event-bus'\n\nComponent({\n  properties: {\n    imageId: {\n      type: String,\n      value: '',\n    },\n    sourcePageId: {\n      type: String,\n      value: '',\n    },\n    list: {\n      type: Array,\n      value: [],\n    },\n  },\n\n  data: {\n    index: 0,\n    tempIndex: -1,\n    pretty: false,\n  },\n\n  observers: {\n    tempIndex(tempIndex) {\n      // 切换图片时会影响前一个页面的图片\n      const { list, sourcePageId } = this.data\n      const image = list[tempIndex]\n      if (image) EventBus.emit(`${sourcePageId}PreviewerChange`, image)\n    },\n  },\n\n  lifetimes: {\n    attached() {\n      const imageId = this.data.imageId     \n      const list = this.data.list\n      let index = 0\n      if (imageId) {\n        const currentIndex = list.findIndex(item => item.id === imageId)\n        if (currentIndex !== -1) index = currentIndex\n      }\n\n      this.setData({ index })\n    },\n\n    detached() {\n      // 告诉前一个页面 previewer 将要销毁\n      EventBus.emit(`${this.data.sourcePageId}PreviewerDestroy`)\n    },\n  },\n\n  methods: {\n    onBeforeRender(evt) {\n      const { index } = evt.detail\n\n      this.data.index = index // 切换可能来自 preview-list 里面，为避免造成循环触发 beforeRender，此处只改 data 不进行 setData\n      this.setData({ tempIndex: index }) // 先更新快速预览栏\n    },\n\n    onTapImage() {\n      this.setData({ pretty: !this.data.pretty })\n    },\n\n    onBack(evt) {\n      this.triggerEvent('back', evt.detail)\n    },\n  },\n})\n"
  },
  {
    "path": "examples/album/components/previewer/preview-home/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {\n    \"preview-list\": \"../preview-list/index\",\n    \"navigation-bar\": \"../../navigation-bar/index\"\n  },\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/album/components/previewer/preview-home/index.wxml",
    "content": "<view class=\"navigation-bar-cnt {{pretty ? 'hide' : 'show'}}\">\n  <share-element key=\"navigation-bar\">\n    <navigation-bar class=\"navigation-bar preview-top-self\" back bindback=\"onBack\"></navigation-bar>\n  </share-element>\n</view>\n<view class=\"preview-extra middle preview-middle-self\"></view>\n<view class=\"preview-cnt\">\n  <view class=\"need-hide-on-back\">\n    <preview-list\n      class=\"preview-list\"\n      index=\"{{index}}\"\n      list=\"{{list}}\"\n      bindbeforerender=\"onBeforeRender\"\n      bindtapimage=\"onTapImage\"\n    ></preview-list>\n  </view>\n  <share-element\n    key=\"{{list[tempIndex] && list[tempIndex].id}}\"\n    class=\"share-element-image need-transform-on-back\"\n    shuttleOnPush=\"to\"\n  >\n    <view class=\"temp-image\">\n      <image src=\"{{list[tempIndex] && list[tempIndex].src}}\" mode=\"aspectFit\"></image>\n    </view>\n  </share-element>\n</view>"
  },
  {
    "path": "examples/album/components/previewer/preview-home/index.wxss",
    "content": ".preview-cnt {\n  position: absolute;\n  left: 0;\n  top: 0;\n  width: 100vw;\n  height: 100vh;\n  z-index: 5;\n}\n\n.preview-list {\n  width: 100vw;\n  height: 100vh;\n  background-color: #fff;\n}\n\n.need-hide-on-back {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100vw;\n  height: 100vh;\n}\n\n.share-element-image {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100vw;\n  height: 100vh;\n  z-index: -1;\n  background-color: transparent;\n}\n\n.share-element-image .temp-image {\n  background-color: transparent;\n  height: 100%;\n  width: 100%;\n}\n\n.share-element-image .temp-image image {\n  width: 100%;\n  height: 100%;\n}\n\n.preview-middle-self {\n  position: absolute;\n  height: 100vh;\n  width: 100vw;\n  z-index: 0;\n  background-color: #fff;\n}\n\n.navigation-bar-cnt {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  z-index: 10;\n  transition: transform .3s ease, opacity .3s ease;\n}\n\n.navigation-bar-cnt.show {\n  transform: none;\n  opacity: 1;\n}\n\n.navigation-bar-cnt.hide {\n  transform: translateY(-50px);\n  opacity: 0;\n}\n\n.navigation-bar {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  z-index: 5;\n}\n"
  },
  {
    "path": "examples/album/components/previewer/preview-image/index.js",
    "content": "import { getShared, getTiming, getRunOnUI, getRunOnJS } from '../../../utils/worklet'\n\n// sharedValues\nconst MOVE_X = 0\nconst MOVE_Y = 1\nconst SCALE = 2\nconst IMG_WIDTH = 3\nconst IMG_HEIGHT = 4\nconst IMG_MAX_SCALE = 5\nconst IMG_MIN_SCALE = 6\nconst IMG_MIN_MOVE_X = 7\nconst IMG_MAX_MOVE_X = 8\nconst IMG_MIN_MOVE_Y = 9\nconst IMG_MAX_MOVE_Y = 10\nconst GESTURE_STATE = 11\nconst TEMP_MOVE_X = 12\nconst TEMP_MOVE_Y = 13\nconst TEMP_SCALE = 14\nconst INIT_MOVE_X = 15\nconst INIT_MOVE_Y = 16\nconst INIT_SCALE = 17\nconst CURRENT_ID = 18\nconst PAGE_ID = 19\nconst RENDERER = 20\n\nconst DEFAULT_IMG_SCALE_MAX = 10\n\nlet screenWidth = 0\nlet screenHeight = 0\n\nfunction adjustTiming(target, renderer, callback) {\n  'worklet'\n  return getTiming(renderer)(target, { duration: 300 }, callback)\n}\n\nfunction recoverTiming(target, renderer, callback) {\n  'worklet'\n  return getTiming(renderer)(target, { duration: 200 }, callback)\n}\n\nfunction vibrateShort(type = 'light') {\n  wx.vibrateShort({ type })\n}\n\nComponent({\n  properties: {\n    status: {\n      type: Number,\n      value: 0, // 1 切到当前，2 当前相邻\n    },\n    image: {\n      type: Object,\n      value: {},\n    },\n  },\n\n  observers: {\n    status(status) {\n      if (!this.isAttached) return\n\n      const oldStatus = this.oldStatus\n      if (status === oldStatus) return\n      this.oldStatus = status\n\n      if (status === 1) {\n        if (this.isImgLoaded) this.triggerEvent('render', this.data.image)\n      } else {\n        this.resetImg()\n      }\n    },\n  },\n\n  lifetimes: {\n    created() {\n      const shared = getShared(this.renderer)\n      \n      this.sharedValues = [\n        shared(0), // MOVE_X\n        shared(0), // MOVE_Y\n        shared(1), // SCALE\n        shared(0), // IMG_WIDTH\n        shared(0), // IMG_HEIGHT\n        shared(1), // IMG_MAX_SCALE\n        shared(1), // IMG_MIN_SCALE\n        shared(0), // IMG_MIN_MOVE_X\n        shared(0), // IMG_MAX_MOVE_X\n        shared(0), // IMG_MIN_MOVE_Y\n        shared(0), // IMG_MAX_MOVE_Y\n        shared(0), // GESTURE_STATE 0 - 初始，1 - 移动图片，2 - 放缩图片，3 - 惯性移动中\n        shared(0), // TEMP_MOVE_X\n        shared(0), // TEMP_MOVE_Y\n        shared(1), // TEMP_SCALE\n        shared(0), // INIT_MOVE_X\n        shared(0), // INIT_MOVE_Y\n        shared(1), // INIT_SCALE\n        shared(''), // CURRENT_ID\n        shared(''), // PAGE_ID\n        shared(this.renderer), // RENDERER\n      ]\n    },\n\n    attached() {\n      this.isAttached = true\n      const pageId = this.getPageId()\n      const id = this.data.image.id\n      const sharedValues = this.sharedValues ?? []\n\n      if (!screenWidth || !screenHeight) {\n        const systemInfo = wx.getSystemInfoSync()\n        screenWidth = systemInfo.screenWidth\n        screenHeight = systemInfo.screenHeight\n      }\n\n      sharedValues[PAGE_ID].value = pageId\n      sharedValues[CURRENT_ID].value = id\n\n      // 应用放缩和拖动\n      this.applyAnimatedStyle('#image', () => {\n        'worklet'\n        return {\n          width: `${sharedValues[IMG_WIDTH].value}px`,\n          height: `${sharedValues[IMG_HEIGHT].value}px`,\n          transform: `translateX(${sharedValues[MOVE_X].value}px) translateY(${sharedValues[MOVE_Y].value}px) translateZ(0px) scale(${sharedValues[SCALE].value})`,\n        }\n      })\n\n      // 计算图片边界\n      function calcImgLimit(scale, imgWidth, imgHeight) {\n        'worklet'\n        const viewWidth = imgWidth * scale\n        const viewHeight = imgHeight * scale\n\n        if (viewWidth > screenWidth) {\n          sharedValues[IMG_MAX_MOVE_X].value = (viewWidth / 2) - (imgWidth / 2)\n          sharedValues[IMG_MIN_MOVE_X].value = screenWidth - (imgWidth / 2) - (viewWidth / 2)\n        } else {\n          sharedValues[IMG_MAX_MOVE_X].value = sharedValues[IMG_MIN_MOVE_X].value = (screenWidth - imgWidth) / 2\n        }\n\n        if (viewHeight > screenHeight) {\n          sharedValues[IMG_MAX_MOVE_Y].value = (viewHeight / 2) - (imgHeight / 2)\n          sharedValues[IMG_MIN_MOVE_Y].value = screenHeight - (imgHeight / 2) - (viewHeight / 2)\n        } else {\n          sharedValues[IMG_MAX_MOVE_Y].value = sharedValues[IMG_MIN_MOVE_Y].value = (screenHeight - imgHeight) / 2\n        }\n      }\n      this.calcImgLimit = calcImgLimit\n\n      // 监听上层的 move/scale 事件\n      getRunOnUI(this.renderer)(() => {\n        'worklet'\n        const renderer = sharedValues[RENDERER].value\n        \n        function adjustImg(args) {\n          const {\n            moveX = null,\n            moveY = null,\n            scale = null,\n            centerX = null,\n            centerY = null,\n            withAnimation = false,\n          } = args\n\n          if (moveX !== null && moveY !== null) {\n            if (!moveX && !moveY) return\n\n            const oldMoveX = sharedValues[MOVE_X].value\n            const oldMoveY = sharedValues[MOVE_Y].value\n            let newMoveX = oldMoveX + moveX\n            let newMoveY = oldMoveY + moveY\n\n            const minMoveX = sharedValues[IMG_MIN_MOVE_X].value\n            const maxMoveX = sharedValues[IMG_MAX_MOVE_X].value\n            const minMoveY = sharedValues[IMG_MIN_MOVE_Y].value\n            const maxMoveY = sharedValues[IMG_MAX_MOVE_Y].value\n\n            const isReachXEdge = (newMoveX <= minMoveX || newMoveX >= maxMoveX)\n            const isReachYEdge = (newMoveY <= minMoveY || newMoveY >= maxMoveY)\n            const isScaleBigger = sharedValues[SCALE].value - sharedValues[INIT_SCALE].value > 0.001\n            const moveGap = Math.abs(moveY) - Math.abs(moveX)\n\n            if (!sharedValues[GESTURE_STATE].value && isReachXEdge && moveGap < 0) {\n              // 图片本身不在移动中 & 到达水平边缘 & 横向移动\n              globalThis.eventBus.emit(`${pageId}Toggle`, args)\n            } else if (!sharedValues[GESTURE_STATE].value && isReachYEdge && moveGap > 0 && moveY > 0) {\n              // 图片本身不在移动中 & 到达垂直边缘 & 纵向移动\n              globalThis.eventBus.emit(`${pageId}Back`, args)\n            } else {\n              // 移动图片\n              const tempOldMoveX = sharedValues[TEMP_MOVE_X].value || oldMoveX\n              const tempOldMoveY = sharedValues[TEMP_MOVE_Y].value || oldMoveY\n\n              if (isReachXEdge) {\n                // 到达水平边缘，增加阻尼\n                const offset = tempOldMoveX - oldMoveX\n                const f = 0.52 * ((1 - Math.min(Math.abs(offset) / screenWidth, 1)) ** 2)\n                newMoveX = oldMoveX + Math.ceil(moveX * f)\n              }\n\n              if (isReachYEdge && isScaleBigger) {\n                // 放大情况下到达垂直边缘，增加阻尼\n                const offset = tempOldMoveY - oldMoveY\n                const f = 0.52 * ((1 - Math.min(Math.abs(offset) / screenHeight, 1)) ** 2)\n                newMoveY = oldMoveY + Math.ceil(moveY * f)\n              }\n\n              if (sharedValues[GESTURE_STATE].value === 3) {\n                // 惯性移动中\n                const tempNewMoveX = Math.min(Math.max(newMoveX, minMoveX), maxMoveX)\n                sharedValues[MOVE_X].value = tempNewMoveX\n                sharedValues[TEMP_MOVE_X].value = tempNewMoveX\n                const tempNewMoveY = Math.min(Math.max(newMoveY, minMoveY), maxMoveY)\n                sharedValues[MOVE_Y].value = tempNewMoveY\n                sharedValues[TEMP_MOVE_Y].value = tempNewMoveY\n              } else {\n                // 水平方向\n                sharedValues[MOVE_X].value = newMoveX\n                const tempNewMoveX = tempOldMoveX + moveX // 这是真正预期的位置，用于阻尼恢复\n                sharedValues[TEMP_MOVE_X].value = Math.min(Math.max(tempNewMoveX, minMoveX), maxMoveX)\n\n                // 垂直方向\n                if (isScaleBigger) {\n                  // 只有放大情况下需要阻尼\n                  sharedValues[MOVE_Y].value = newMoveY\n                  const tempNewMoveY = tempOldMoveY + moveY // 这是真正预期的位置，用于阻尼恢复\n                  sharedValues[TEMP_MOVE_Y].value = Math.min(Math.max(tempNewMoveY, minMoveY), maxMoveY)\n                } else {\n                  const tempNewMoveY = Math.min(Math.max(newMoveY, minMoveY), maxMoveY)\n                  sharedValues[MOVE_Y].value = tempNewMoveY\n                  sharedValues[TEMP_MOVE_Y].value = tempNewMoveY\n                }\n\n                sharedValues[GESTURE_STATE].value = 1\n                globalThis.eventBus.emit(`${pageId}Moving`)\n              }\n            }\n          } else if (scale !== null && centerX !== null && centerY !== null) {\n            const oldMoveX = sharedValues[MOVE_X].value\n            const oldMoveY = sharedValues[MOVE_Y].value\n            const oldScale = sharedValues[SCALE].value\n            const newScale = oldScale * scale\n            const tempNewScale = Math.min(Math.max(newScale, sharedValues[IMG_MIN_SCALE].value), sharedValues[IMG_MAX_SCALE].value) // 这是真正预期的 scale，用于阻尼恢复\n            const realScale = tempNewScale / oldScale\n\n            // 其实就是求放缩时图片中心的偏移变化\n            const imgWidth = sharedValues[IMG_WIDTH].value\n            const imgHeight = sharedValues[IMG_HEIGHT].value\n            const gapX = centerX - (imgWidth / 2)\n            let newMoveX = gapX - ((gapX - oldMoveX) * realScale)\n            const gapY = centerY - (imgHeight / 2)\n            let newMoveY = gapY - ((gapY - oldMoveY) * realScale)\n\n            calcImgLimit(tempNewScale, imgWidth, imgHeight)\n            newMoveX = Math.min(Math.max(newMoveX, sharedValues[IMG_MIN_MOVE_X].value), sharedValues[IMG_MAX_MOVE_X].value)\n            newMoveY = Math.min(Math.max(newMoveY, sharedValues[IMG_MIN_MOVE_Y].value), sharedValues[IMG_MAX_MOVE_Y].value)\n\n            sharedValues[MOVE_X].value = withAnimation ? adjustTiming(newMoveX, renderer) : newMoveX\n            sharedValues[MOVE_Y].value = withAnimation ? adjustTiming(newMoveY, renderer) : newMoveY\n            if (withAnimation) {\n              // 有动画，表示是双击放大，不需要考虑阻尼\n              sharedValues[SCALE].value = adjustTiming(tempNewScale, renderer, () => {\n                sharedValues[GESTURE_STATE].value = 0 // 动画结束，状态恢复初始\n              })\n              sharedValues[TEMP_SCALE].value = tempNewScale\n            } else {\n              let calcNewScale = newScale\n              if (Math.abs(newScale - tempNewScale) > 0.001) {\n                // 超出了边界，补充阻尼计算\n                const gap = newScale - oldScale\n                const dampingLimit = gap < 0 ? (newScale / tempNewScale) : (tempNewScale / newScale)\n                const f = (gap < 0 ? 0.3 : 0.6) * (Math.min(dampingLimit, 1) ** 2) // 不要问为什么是这个值，凭感觉定的\n                calcNewScale = oldScale + (gap  * f)\n              }\n\n              sharedValues[SCALE].value = calcNewScale\n              sharedValues[TEMP_SCALE].value = tempNewScale\n            }\n\n            // 回弹是延迟的，所以这里需要确保没有回弹\n            sharedValues[TEMP_MOVE_X].value = newMoveX\n            sharedValues[TEMP_MOVE_Y].value = newMoveY\n\n            sharedValues[GESTURE_STATE].value = 2\n            globalThis.eventBus.emit(`${pageId}Moving`)\n          }\n        }\n\n        // 监听图片拖动手势\n        if (!globalThis.temp[`${pageId}${id}ImgMove`]) {\n          globalThis.temp[`${pageId}${id}ImgMove`] = args => adjustImg(args)\n          globalThis.eventBus.on(`${pageId}${id}Move`, globalThis.temp[`${pageId}${id}ImgMove`])\n        }\n\n        // 监听图片放缩手势\n        if (!globalThis.temp[`${pageId}${id}ImgScale`]) {\n          globalThis.temp[`${pageId}${id}ImgScale`] = args => adjustImg(args)\n          globalThis.eventBus.on(`${pageId}${id}Scale`, globalThis.temp[`${pageId}${id}ImgScale`])\n        }\n\n        // 监听手势结束事件\n        if (!globalThis.temp[`${pageId}${id}ImgEnd`]) {\n          globalThis.temp[`${pageId}${id}ImgEnd`] = args => {\n            // 手势结束\n            sharedValues[GESTURE_STATE].value = 0\n\n            const moveX = sharedValues[MOVE_X].value\n            const targetMoveX = sharedValues[TEMP_MOVE_X].value\n            const needRecoverX = Math.abs(moveX - targetMoveX) > 0.001\n            const moveY = sharedValues[MOVE_Y].value\n            const targetMoveY = sharedValues[TEMP_MOVE_Y].value\n            const needRecoverY = Math.abs(moveY - targetMoveY) > 0.001\n            const scale = sharedValues[SCALE].value\n            const targetScale = sharedValues[TEMP_SCALE].value\n            const needRecoverScale = Math.abs(scale - targetScale) > 0.001\n\n            const isScaleBigger = sharedValues[SCALE].value - sharedValues[INIT_SCALE].value > 0.001\n            const { velocityX, velocityY } = args\n            const vx = Math.min(Math.abs(velocityX), 700)\n            const vy = Math.min(Math.abs(velocityY), 700)\n\n            if (needRecoverX || needRecoverY || needRecoverScale) {\n              // 阻尼回弹\n              if (needRecoverX) sharedValues[MOVE_X].value = recoverTiming(targetMoveX, renderer)\n              if (needRecoverY) sharedValues[MOVE_Y].value = recoverTiming(targetMoveY, renderer)\n              if (needRecoverScale) {\n                sharedValues[SCALE].value = recoverTiming(targetScale, renderer)\n                getRunOnJS(sharedValues[RENDERER].value)(vibrateShort)()\n              }\n            } else if (isScaleBigger && (vx > 50 || vy > 50)) {\n              // 惯性计算\n              sharedValues[GESTURE_STATE].value = 3\n\n              const a = -0.0015\n              const directionX = Math.sign(velocityX)\n              const directionY = Math.sign(velocityY)\n              let vx0 = vx / 1000\n              let vy0 = vy / 1000\n              const t = 16\n\n              const nextTick = () => {\n                'worklet'\n                // 如果此时进入其他状态，则取消惯性\n                if (sharedValues[GESTURE_STATE].value !== 3) return\n\n                let moveX = 0\n                let moveY = 0\n\n                if (vx0 > 0) {\n                  // 还有水平速度\n                  const vx1 = a * t + vx0\n                  if (vx1 > 0) {\n                    const s = (vx0 * t) + ((a * (t ** 2)) / 2)\n                    moveX = s * directionX\n                    vx0 = vx1\n                  } else {\n                    vx0 = 0\n                  }\n                }\n\n                if (vy0 > 0) {\n                  // 还有\b垂直速度\n                  const vy1 = a * t + vy0\n                  if (vy1 > 0) {\n                    const s = (vy0 * t) + ((a * (t ** 2)) / 2)\n                    moveY = s * directionY\n                    vy0 = vy1\n                  } else {\n                    vy0 = 0\n                  }\n                }\n\n                if (moveX || moveY) {\n                  adjustImg({ moveX, moveY })\n                  setTimeout(nextTick, t)\n                } else {\n                  // 回归初始状态\n                  sharedValues[GESTURE_STATE].value = 0\n                }\n              }\n\n              setTimeout(nextTick, t)\n            }\n          }\n          globalThis.eventBus.on(`${pageId}${id}End`, globalThis.temp[`${pageId}${id}ImgEnd`])\n        }\n\n        // 监听图片双击手势\n        if (!globalThis.temp[`${pageId}${id}ImgDoubleTap`]) {\n          globalThis.temp[`${pageId}${id}ImgDoubleTap`] = args => adjustImg(args)\n          globalThis.eventBus.on(`${pageId}${id}DoubleTap`, globalThis.temp[`${pageId}${id}ImgDoubleTap`])\n        }\n      })()\n    },\n\n    detached() {\n      this.isAttached = false\n      const id = this.data.image.id\n      const pageId = this.getPageId()\n\n      getRunOnUI(this.renderer)(() => {\n        'worklet'\n        const removeList = ['Move', 'Scale', 'End', 'DoubleTap']\n        removeList.forEach(item => {\n          'worklet'\n          const globalKey = `${pageId}${id}Img${item}`\n          if (globalThis.temp[globalKey]) {\n            globalThis.eventBus.off(`${pageId}${id}${item}`, globalThis.temp[globalKey])\n            delete globalThis.temp[globalKey]\n          }\n        })\n      })()\n    },\n  },\n\n  methods: {\n    resetImg() {\n      const sharedValues = this.sharedValues ?? []\n      const initMoveX = sharedValues[INIT_MOVE_X].value\n      const initMoveY = sharedValues[INIT_MOVE_Y].value\n      const initScale = sharedValues[INIT_SCALE].value\n\n      this.calcImgLimit(initScale, sharedValues[IMG_WIDTH].value, sharedValues[IMG_HEIGHT].value)\n\n      sharedValues[MOVE_X].value = initMoveX\n      sharedValues[MOVE_Y].value = initMoveY\n      sharedValues[TEMP_MOVE_X].value = initMoveX\n      sharedValues[TEMP_MOVE_Y].value = initMoveY\n      sharedValues[SCALE].value = initScale\n      sharedValues[TEMP_SCALE].value = initScale\n    },\n\n    onImgLoad(evt) {\n      const { width: imgWidth, height: imgHeight } = evt.detail\n      const sharedValues = this.sharedValues ?? []\n\n      if (!screenWidth || !screenHeight) {\n        const systemInfo = wx.getSystemInfoSync()\n        screenWidth = systemInfo.screenWidth\n        screenHeight = systemInfo.screenHeight\n      }\n      const windowRatio = screenWidth / screenHeight\n\n      sharedValues[IMG_WIDTH].value = imgWidth\n      sharedValues[IMG_HEIGHT].value = imgHeight\n\n      if (!imgWidth || !imgHeight) return\n\n      const imgRatio = imgWidth / imgHeight\n\n      let scale = 1\n      let moveX = 0\n      let moveY = 0\n\n      if (imgRatio > windowRatio) {\n        scale = screenWidth / imgWidth\n        moveX = (screenWidth - imgWidth) / 2\n        const scaleHeight = imgHeight * scale\n        moveY = ((screenHeight - scaleHeight) / 2) - ((imgHeight - scaleHeight) / 2)\n\n        sharedValues[IMG_MAX_SCALE].value = imgWidth > screenWidth ? 2 : DEFAULT_IMG_SCALE_MAX\n        sharedValues[IMG_MIN_SCALE].value = imgWidth > screenWidth ? screenWidth / imgWidth : 1\n      } else {\n        scale = screenHeight / imgHeight\n        moveY = (screenHeight - imgHeight) / 2\n        const scaleWidth = imgWidth * scale\n        moveX = ((screenWidth - scaleWidth) / 2) - ((imgWidth - scaleWidth) / 2)\n\n        sharedValues[IMG_MAX_SCALE].value = imgHeight > screenHeight ? 2 : DEFAULT_IMG_SCALE_MAX\n        sharedValues[IMG_MIN_SCALE].value = imgHeight > screenHeight ? screenHeight / imgHeight : 1\n      }\n\n      this.calcImgLimit(scale, imgWidth, imgHeight)\n\n      sharedValues[MOVE_X].value = moveX\n      sharedValues[MOVE_Y].value = moveY\n      sharedValues[TEMP_MOVE_X].value = moveX\n      sharedValues[TEMP_MOVE_Y].value = moveY\n      sharedValues[SCALE].value = scale\n      sharedValues[TEMP_SCALE].value = scale\n\n      sharedValues[INIT_MOVE_X].value = moveX\n      sharedValues[INIT_MOVE_Y].value = moveY\n      sharedValues[INIT_SCALE].value = scale\n\n      this.isImgLoaded = true\n\n      if (this.data.status === 1) {\n        // 处于 current 状态，直接扔 render 事件\n        this.triggerEvent('render', this.data.image)\n      }\n    },\n\n    onDoubleTap(evt) {\n      'worklet'\n      const sharedValues = this.sharedValues ?? []\n      const currentScale = sharedValues[SCALE].value\n      const minScale = sharedValues[IMG_MIN_SCALE].value\n      globalThis.eventBus.emit(`${sharedValues[PAGE_ID].value}${sharedValues[CURRENT_ID].value}DoubleTap`, {\n        scale: currentScale <= minScale ? 2 : 0.0001, // 如果放大过，就缩回默认大小；如果没有放大过则放大\n        centerX: evt.absoluteX,\n        centerY: evt.absoluteY,\n        withAnimation: true,\n      })\n    },\n  },\n})\n"
  },
  {
    "path": "examples/album/components/previewer/preview-image/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {},\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/album/components/previewer/preview-image/index.wxml",
    "content": "<double-tap-gesture-handler class=\"double-tap-gesture-handler\" worklet:ongesture=\"onDoubleTap\">\n  <view id=\"image\" class=\"image-wrapper\">\n    <image\n      wx:if=\"{{status}}\"\n      class=\"image\"\n      src=\"{{image.src}}\"\n      mode=\"aspectFit\"\n      bindload=\"onImgLoad\"\n    ></image>\n  </view>\n</double-tap-gesture-handler>\n"
  },
  {
    "path": "examples/album/components/previewer/preview-image/index.wxss",
    "content": ".double-tap-gesture-handler {\n  display: block;\n  width: 100%;\n  height: 100%;\n}\n\n.image-wrapper {\n  position: absolute;\n  left: 0;\n  top: 0;\n  width: 100%;\n  height: 100%;\n  transform-origin: center;\n  will-change: transform;\n}\n\n.image {\n  width: 100%;\n  height: 100%;\n}\n"
  },
  {
    "path": "examples/album/components/previewer/preview-list/index.js",
    "content": "import { getShared, getRunOnUI, getRunOnJS } from '../../../utils/worklet'\n\nconst PreviewerGesture = {\n  Init: 0, // 初始\n  Moving: 1, // 移动图片\n  Toggle: 2, // 切换图片\n  Back: 3, // 退出页面\n}\n\n// sharedValues\nconst GESTURE_STATE = 0\nconst CURRENT_ID = 1\nconst RENDERER = 2\n\nComponent({\n  properties: {\n    index: {\n      type: Number,\n      value: 0,\n    },\n    list: {\n      type: Array,\n      value: [],\n    },\n  },\n\n  data: {\n    currentIndex: -99,\n    needSwiperAnimation: true,\n  },\n\n  observers: {\n    list(list) {\n      const index = this.data.index\n      const current = list[index]\n      if (!current) return\n\n      const sharedValues = this.sharedValues ?? []\n      sharedValues[CURRENT_ID].value = current.id\n\n      this.toggleImage(index, true)\n    },\n\n    index(index) {\n      const len = this.data.list.length\n      if (!len) return\n\n      if (index !== this.data.currentIndex && index >= 0 && index < len) {\n        this.toggleImage(index, true)\n      } else {\n        // 设置相同的 index 也给一下 render 事件\n        const image = this.data.list[index]\n        if (image && image.id === this.currentRenderImage) {\n          // 要求 image 已经渲染完成\n          this.onImageRender({ detail: image })\n        }\n      }\n    },\n  },\n\n  lifetimes: {\n    created() {\n      const shared = getShared(this.renderer)\n\n      this.sharedValues = [\n        shared(0), // GESTURE_STATE\n        shared(''), // CURRENT_ID\n        shared(this.renderer), // RENDERER\n      ]\n    },\n\n    async attached() {\n      const pageId = this.getPageId()\n      const sharedValues = this.sharedValues ?? []\n\n      getRunOnUI(this.renderer)(() => {\n        'worklet'\n        // 监听拖拽返回手势\n        if (!globalThis.temp[`${pageId}PreviewerBack`]) {\n          globalThis.temp[`${pageId}PreviewerBack`] = () => sharedValues[GESTURE_STATE].value = PreviewerGesture.Back\n          globalThis.eventBus.on(`${pageId}Back`, globalThis.temp[`${pageId}PreviewerBack`])\n        }\n\n        // 监听图片切换手势\n        if (!globalThis.temp[`${pageId}PreviewerToggle`]) {\n          globalThis.temp[`${pageId}PreviewerToggle`] = () => sharedValues[GESTURE_STATE].value = PreviewerGesture.Toggle\n          globalThis.eventBus.on(`${pageId}Toggle`, globalThis.temp[`${pageId}PreviewerToggle`])\n        }\n\n        // 监听图片拖动中事件\n        if (!globalThis.temp[`${pageId}PreviewerMoving`]) {\n          globalThis.temp[`${pageId}PreviewerMoving`] = () => sharedValues[GESTURE_STATE].value = PreviewerGesture.Moving\n          globalThis.eventBus.on(`${pageId}Moving`, globalThis.temp[`${pageId}PreviewerMoving`])\n        }\n\n        // 监听图片拖动手势\n        if (!globalThis.temp[`${pageId}PreviewerMove`]) {\n          globalThis.temp[`${pageId}PreviewerMove`] = args => {\n            // 此处只做转发\n            const currentId = sharedValues[CURRENT_ID].value\n            globalThis.eventBus.emit(`${pageId}${currentId}Move`, args)\n          }\n          globalThis.eventBus.on(`${pageId}Move`, globalThis.temp[`${pageId}PreviewerMove`])\n        }\n\n        // 监听图片放缩手势\n        if (!globalThis.temp[`${pageId}PreviewerScale`]) {\n          globalThis.temp[`${pageId}PreviewerScale`] = args => {\n            // 此处只做转发\n            const currentId = sharedValues[CURRENT_ID].value\n            globalThis.eventBus.emit(`${pageId}${currentId}Scale`, args)\n          }\n          globalThis.eventBus.on(`${pageId}Scale`, globalThis.temp[`${pageId}PreviewerScale`])\n        }\n\n        // 监听手势结束事件\n        if (!globalThis.temp[`${pageId}PreviewerEnd`]) {\n          globalThis.temp[`${pageId}PreviewerEnd`] = args => {\n            // 此处只做转发\n            const currentId = sharedValues[CURRENT_ID].value\n            globalThis.eventBus.emit(`${pageId}${currentId}End`, args)\n          }\n          globalThis.eventBus.on(`${pageId}End`, globalThis.temp[`${pageId}PreviewerEnd`])\n        }\n      })()\n    },\n\n    detached() {\n      const pageId = this.getPageId()\n      getRunOnUI(this.renderer)(() => {\n        'worklet'\n        const removeList = ['Back', 'Toggle', 'Moving', 'Move', 'Scale', 'End']\n        removeList.forEach(item => {\n          'worklet'\n          const globalKey = `${pageId}Previewer${item}`\n          if (globalThis.temp[globalKey]) {\n            globalThis.eventBus.off(`${pageId}${item}`, globalThis.temp[globalKey])\n            delete globalThis.temp[globalKey]\n          }\n        })\n      })()\n    },\n  },\n\n  methods: {\n    async toggleImage(index, disableAnimation = false) {\n      const image = this.data.list[index]\n      if (!image) return\n\n      const sharedValues = this.sharedValues ?? []\n      sharedValues[CURRENT_ID].value = image.id\n\n      this.setData({\n        currentIndex: index,\n        needSwiperAnimation: !disableAnimation,\n      })\n      this.data.index = index // index 也更新一下，方便其他地方取用\n\n      this.triggerEvent('beforerender', { index, image })\n    },\n\n    onImageRender(evt) {\n      const list = this.data.list\n      const index = this.data.currentIndex\n      const image = list[index] || {}\n\n      if (evt.detail.id !== image.id) return\n\n      this.currentRenderImage = image.id\n    },\n\n    onTapImage() {\n      'worklet'\n      getRunOnJS(this.sharedValues[RENDERER].value)(this.triggerEvent.bind(this))('tapimage')\n    },\n\n    shouldResponseOnMove() {\n      'worklet'\n      // 当触发图片切换手势时，才让 swiper 工作\n      const sharedValues = this.sharedValues ?? []\n      return sharedValues[GESTURE_STATE].value === PreviewerGesture.Toggle\n    },\n\n    onSwiperChange(evt) {\n      const { current, source } = evt.detail\n      if (source === 'touch') this.toggleImage(current, false)\n    },\n  },\n})\n"
  },
  {
    "path": "examples/album/components/previewer/preview-list/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {\n    \"preview-image\": \"../preview-image/index\"\n  },\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/album/components/previewer/preview-list/index.wxml",
    "content": "<view class=\"image-previewer\">\n  <horizontal-drag-gesture-handler\n    tag=\"swiper\"\n    simultaneousHandlers=\"{{['scale']}}\"\n    native-view=\"swiper\"\n    worklet:should-response-on-move=\"shouldResponseOnMove\"\n  >\n    <tap-gesture-handler worklet:ongesture=\"onTapImage\">\n      <swiper\n        class=\"swiper-cnt\"\n        current=\"{{currentIndex}}\"\n        duration=\"300\"\n        cache-extent=\"2\"\n        scrollWithAnimation=\"{{needSwiperAnimation}}\"\n        bindchange=\"onSwiperChange\"\n      >\n        <swiper-item wx:for=\"{{list}}\" wx:key=\"id\">\n          <preview-image\n            class=\"image\"\n            status=\"{{currentIndex === index ? 1 : ((index >= currentIndex - 1 && index <= currentIndex + 1) ? 2 : 0)}}\"\n            image=\"{{item}}\"\n            bindrender=\"onImageRender\"\n          ></preview-image>\n        </swiper-item>\n      </swiper>\n    </tap-gesture-handler>\n  </horizontal-drag-gesture-handler>\n</view>\n"
  },
  {
    "path": "examples/album/components/previewer/preview-list/index.wxss",
    "content": "\n.image-previewer {\n  width: 100vw;\n  height: 100vh;\n  overflow: hidden;\n}\n\n.swiper-cnt {\n  display: flex;\n  flex-direction: row;\n  position: absolute;\n  left: 0;\n  top: 0;\n  width: 100vw;\n  height: 100vh;\n  z-index: 20;\n}\n\n.image {\n  display: block;\n  width: 100%;\n  height: 100%;\n}\n"
  },
  {
    "path": "examples/album/pages/album/index.js",
    "content": "import { getAlbum } from '../../utils/store'\n\nPage({\n  data: {\n    imageWidth: 0,\n    imageMargin: 12, // 图片间距\n    lineLimit: 3, // 每行多少张图片\n    list: [],\n  },\n\n  onLoad() {\n    const { imageMargin, lineLimit } = this.data\n    const { screenWidth } = wx.getSystemInfoSync()\n    this.setData({\n      imageWidth: (screenWidth - imageMargin * 4) / lineLimit, // 图片宽度\n      list: getAlbum(),\n    })\n  },\n})"
  },
  {
    "path": "examples/album/pages/album/index.json",
    "content": "{\n  \"usingComponents\": {\n    \"navigation-bar\": \"../../components/navigation-bar/index\",\n    \"album\": \"../../components/album/index\"\n  },\n  \"disableScroll\": true,\n  \"navigationStyle\": \"custom\"\n}"
  },
  {
    "path": "examples/album/pages/album/index.wxml",
    "content": "<view class=\"cnt\">\n  <share-element key=\"navigation-bar\">\n    <navigation-bar title=\"相册图片预览\" back></navigation-bar>\n  </share-element>\n  <album\n    class=\"album\"\n    list=\"{{list}}\"\n    imageWidth=\"{{imageWidth}}\"\n    imageMargin=\"{{imageMargin}}\"\n    lineLimit=\"{{lineLimit}}\"\n  ></album>\n  <view class=\"safe-area-inset-bottom\"></view>\n</view>\n"
  },
  {
    "path": "examples/album/pages/album/index.wxss",
    "content": ".cnt {\n  position: absolute;\n  top: 0;\n  width: 100%;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n}\n\n.album {\n  height: 0;\n  flex: 1;\n  margin-bottom: 12px;\n}\n\n.safe-area-inset-bottom {\n  height: env(safe-area-inset-bottom);\n}\n"
  },
  {
    "path": "examples/album/pages/preview/index.js",
    "content": "import { getAlbum } from '../../utils/store'\n\nComponent({\n  properties: {\n    imageid: {\n      type: String,\n      value: '',\n    },\n    sourcepageid: {\n      type: String,\n      value: '',\n    },\n  },\n\n  data: {\n    imageId: '',\n    sourcePageId: '',\n    list: [],\n  },\n\n  lifetimes: {\n    attached() {\n      // 因为页面的 onLoad 太迟，所以选用 component 构造器的 attached 生命周期来设置 shareKey，确保 share-element 动画正常执行\n      const query = this.data\n      const imageId = decodeURIComponent(query.imageid || '')\n      const sourcePageId = decodeURIComponent(query.sourcepageid || '')\n      const list = getAlbum()\n      this.setData({ imageId, sourcePageId, list })\n    },\n  },\n})\n"
  },
  {
    "path": "examples/album/pages/preview/index.json",
    "content": "{\n  \"usingComponents\": {\n    \"previewer\": \"../../components/previewer/index\"\n  },\n  \"navigationStyle\": \"custom\",\n  \"backgroundColorContent\": \"#00000000\",\n  \"disableScroll\": true,\n  \"componentFramework\": \"glass-easel\",\n  \"renderer\": \"skyline\"\n}\n"
  },
  {
    "path": "examples/album/pages/preview/index.wxml",
    "content": "<view class=\"box-cotainer\">\n  <previewer\n    imageId=\"{{imageId}}\"\n    sourcePageId=\"{{sourcePageId}}\"\n    list=\"{{list}}\"\n  ></previewer>\n</view>"
  },
  {
    "path": "examples/album/pages/preview/index.wxss",
    "content": "page {\n  background-color: transparent;\n}\n\n.box-cotainer {\n  width: 100vw;\n  height: 100vh;\n  position: relative;\n}\n\n.previewer {\n  width: 100%;\n  height: 100%;\n}\n"
  },
  {
    "path": "examples/album/project.config.json",
    "content": "{\n  \"description\": \"项目配置文件\",\n  \"packOptions\": {\n    \"ignore\": [],\n    \"include\": []\n  },\n  \"setting\": {\n    \"bundle\": false,\n    \"userConfirmedBundleSwitch\": false,\n    \"urlCheck\": true,\n    \"scopeDataCheck\": false,\n    \"coverView\": true,\n    \"es6\": true,\n    \"postcss\": true,\n    \"compileHotReLoad\": false,\n    \"lazyloadPlaceholderEnable\": false,\n    \"preloadBackgroundData\": false,\n    \"minified\": true,\n    \"autoAudits\": false,\n    \"newFeature\": false,\n    \"uglifyFileName\": false,\n    \"uploadWithSourceMap\": true,\n    \"useIsolateContext\": true,\n    \"nodeModules\": false,\n    \"enhance\": true,\n    \"useMultiFrameRuntime\": true,\n    \"useApiHook\": true,\n    \"useApiHostProcess\": true,\n    \"showShadowRootInWxmlPanel\": true,\n    \"packNpmManually\": false,\n    \"enableEngineNative\": false,\n    \"packNpmRelationList\": [],\n    \"minifyWXSS\": true,\n    \"showES6CompileOption\": false,\n    \"minifyWXML\": true,\n    \"babelSetting\": {\n      \"ignore\": [],\n      \"disablePlugins\": [],\n      \"outputPath\": \"\"\n    },\n    \"condition\": false,\n    \"skylineRenderEnable\": false,\n    \"compileWorklet\": true\n  },\n  \"compileType\": \"miniprogram\",\n  \"libVersion\": \"latest\",\n  \"appid\": \"wxe5f52902cf4de896\",\n  \"projectname\": \"album-demo\",\n  \"condition\": {},\n  \"editorSetting\": {\n    \"tabIndent\": \"insertSpaces\",\n    \"tabSize\": 2\n  }\n}"
  },
  {
    "path": "examples/album/project.private.config.json",
    "content": "{\n  \"description\": \"项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档：https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html\",\n  \"projectname\": \"album\",\n  \"setting\": {\n    \"compileHotReLoad\": false,\n    \"skylineRenderEnable\": true\n  },\n  \"libVersion\": \"latest\"\n}"
  },
  {
    "path": "examples/album/sitemap.json",
    "content": "{\n  \"desc\": \"关于本文件的更多信息，请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html\",\n  \"rules\": [{\n  \"action\": \"allow\",\n  \"page\": \"*\"\n  }]\n}"
  },
  {
    "path": "examples/album/utils/event-bus.js",
    "content": "import { getRunOnUI } from './worklet'\n\nconst map = {}\n\nfunction on(eventName, handler) {\n  map[eventName] = map[eventName] || []\n  map[eventName].push(handler)\n}\n\nfunction off(eventName, handler) {\n  const handlerList = map[eventName]\n  if (!handlerList || !handlerList.length) return\n\n  const index = handlerList.indexOf(handler)\n  if (index !== -1) handlerList.splice(index, 1)\n}\n\nfunction emit(eventName, ...args) {\n  const handlerList = map[eventName]\n  if (!handlerList || !handlerList.length) return\n\n  for (let i = handlerList.length - 1; i >= 0; i--) {\n    handlerList[i](...args)\n  }\n}\n\nfunction initWorkletEventBus(renderer) {\n  getRunOnUI(renderer)(() => {\n    'worklet'\n    if (!globalThis.temp) globalThis.temp = {}\n    if (!globalThis.eventBus) {\n      const eventBus = {\n        map: {},\n        on(eventName, handler) {\n          eventBus.map[eventName] = eventBus.map[eventName] || []\n          eventBus.map[eventName].push(handler)\n        },\n        off(eventName, handler) {\n          const handlerList = eventBus.map[eventName]\n          if (!handlerList || !handlerList.length) return\n\n          const index = handlerList.indexOf(handler)\n          if (index !== -1) handlerList.splice(index, 1)\n        },\n        emit(eventName, args) {\n          const handlerList = eventBus.map[eventName]\n          if (!handlerList || !handlerList.length) return\n\n          for (let i = handlerList.length - 1; i >= 0; i--) {\n            handlerList[i](args)\n          }\n        },\n      }\n      globalThis.eventBus = eventBus\n    }\n  })()\n}\n\nexport default {\n  on,\n  off,\n  emit,\n  initWorkletEventBus,\n}\n"
  },
  {
    "path": "examples/album/utils/store.js",
    "content": "const imageList = []\nexport function getAlbum() {\n  if (!imageList.length) {\n    const srcList = [\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYcv7mQABQVg50gh51-Kb1mlBq0c8Difu3rXn0ldrZdZwVx9REPbKVyZb3E9Wq6YFLA',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYVtRBEXZ74JLECYN4Hc3Cdml3x7cJcbcBj3sIdYb2L_7DJ14X8TAHiZ7Ydct52WIYA',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYfaqohk6ndcC6_CBkUZszfSpKbqUAV7S2xWRbAQ459YsPWAmLKkicEOPS1L3NmnnRA',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYYa7k4CAG71Ci0SRPExPB1sGiiVPcYwSD5CAYgq8Ni2RhGQlqIrIwnBlt90mUzL7Lg',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYYjda9Dp372N3T05q_nn3PgvoXBoReXvaXBfkthtXQLN7m5_YI6FoTre-xvJBDFLMA',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYTIqRBCdQgzZm3KO3usZT7zM2O0EoylisontlH4TZAC_qfVjFVU4L4CPjLm0mppQwg',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYfa6mRnywhNbBFV5eAt7oTz3zjlNJeujfQx0PVA1ufenPHBvxYXRNJ5chyi6RPaE7A',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYYY1OalScOn4EMcQpkPaJ1Sxhri8CScjnhqVfjAZnLuVFl0JAM4VziHhSzHLZXtAaQ',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYSPfXw5qxNvP3f5uJhy0kdkaTAbIZ187IFWmBiluEs8Puw0tXgBlBgGKN-irLPOIDw',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYZB1p48LLH-Pc7Rzr4nN0YF-uZg7FW7zksw_Kjp0BNDHcZp9R9SRKbg0rA1HBaeK3Q',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYYXMoGlbo8DkSiia6d-_3Dv15DjMGCEhBkPYd5BrNkSUTPtomAm0CVeHgC244sapKw',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYf3q0W302-kseD8VxLKoItZ6HgneLkgpQSEMIgEKz_xVE7putZxs2YEYqB13Uh37_w',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYRu0VRyVvePJ4pB4_Dvj0ytF-ovjQzMl6WMLyuCeKk3579HNjKLIeNrHE7OprTBx5w',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYc-vygvNNpz1fcZZjZ6B0Jm0X2dpOWJBn4u4T15gwL-1Qr70v6fkFgUswldiPhQG0Q',\n      'https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYUo-SgGpk2gpFNixuaCMxDadRvvWxqC5jc1ma-oobJf4mWVg3F3iRt1Bkv-sR0l3-Q',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJCk-lm-hNrvdJ9paSPqTXkCKLE6eIuc6zAs3Lr1Qh6jEMxdfgP_rvTftB0SQiZeQNQ',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJImpE8Sm-oMoeJizjFr8mxotXQSFlSm8ZUD6GLf_ptM5ozLvnc2eczwSN09Y5M0ovg',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJBAK7KJg9KId_6N1-rJ4OyCCQoJGVMOaTabboo2viRucoxkPvHRkn2fVl6tectlzBg',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJGVi_kGQppwXClflw43wtANVzuw0_Y8ij9VzW1O8hr6cR195D87X7zQS_1gjRo8IdQ',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJHhlMVj_svRRdDMHjtAvwGwU2d_VAJ-y3SigDx2XjjA22Y63oStS-fw5gooV5rX42Q',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJPRaN5CDI6NZFg_qbSxeqF8UBpM4lXJ_1o9S9bsOOxMpuXGLeKyAKleWlAXmVLmQOw',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJMQYuLq5q6_CWdtW3RnWbYMaNhzew60nGsQfBtyeogj3vRfGimJBi87ThS577HC6ag',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJBalQKoYXBze_mRhoUHIh5OfHiR95JEa18ob-y7uu8W1gpOjO87BbOcki3xM_RrO1Q',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJAqHss14EPHStg3OpHjtIYzOGQgxvsDcACMxQc2waTpAMsJBFxVlp1JkZQJy2gXu3g',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJH2f0R4uXyqnNGlrivO8cKbn0nz1DE_6s22rc91zluwIrqiAVZNREvCeVYAUS8aaZw',\n      'https://res.wx.qq.com/op_res/SPJ17foR6CQ_1kS3N3iWEk2lg3OAXWsGLLFfwHR64LgMfJQm0jK0YWSG9rVD9cysd0AddG5ZhacUE5mm2jPelQ',\n    ]\n    const thumbnailList = [\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJDaOeA5r3yDpl5AjlRxMmKPaX0WbJIRr0PjZzsfxIGq1n5Q0o65EpxFGpGpbT9e8QQ',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJACGFAh31lY9OxoD1iSXsyYpk2adWITN6b7gT7RcBEeDXhppBLYI3JhSMJDyIvPrcg',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJJX5J79IIhayRchEZTln15RdpFkFfsqsvng5dqMa12Vm9rmcT-hhw1alfHvxTXVVRw',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJApluxMePb-2yj-XeDSDlr-pNZnbKpaNwl4BUXyZ0bKXWMcqKQSGDdOWFPOP08-4Sw',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJI8Xazme5-MIIJYUg9t2ibxTp-PtGPUXes9XZj8rRIfyhZJgyfejqwzwpSDBh-K5eg',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJHOt1gRAQQbf2Bx8mJoYj_8hoN17WusJ_kEzxqYbkN6X1dRjDMdp0gS9jJf-VAezfg',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJNLlVMP9qArsQkkz294mrVFbI2pxwKxf0I1F8S86W4zZ8bcpuDv4xIWioikrD-85Dw',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJOY6-yX_tVBt6ZC4ffTUqdnMXaCEKf0mYb8ebfl0l-VUKHzayRrbipPLqsynrTN0FA',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJMgKiLq4XOYd9BtY9HCvD4BvuYDo9ro9XBb3_fQk90z8odHLs4eYvoHwkyKaM3ztZA',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJH3eO7DeTKRV0Ys2vZ2aqUom-xWvymxzS3uiu06syi2vlj8hzWyIK1_inNHTVkuWPw',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJA7-uaVSCPgd7Q32x1_vmHrDLaretZxRboZv2wZiNBkRdgihEvRhcWRHrm0vBUggzw',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJBUmVY1MDz1vsXTJlQCPd-fHF8AsVCi6yVK4BxhjBXs085BQZaI6LoWW4GfnqMRqbg',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJKh4kMbGYKCPnBu-ZWsQnn3NEFKDEN0DgdKH_R9v8Snkp-cfiU8dZd_abw5QcyiWBQ',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJKPDdtcGoJzhB8HdWlDtWaxeIOvdYSmQoihkYQPrHDoGr2PJD31RPzqcz5u8b3rpoQ',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJFryDUHWInyL4LeRP6y_wIUpf7kQSfrcGPcGeZbLuOtan8ml9SbJ1k0Qdgelf6iEBg',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJE1YvpBMZCjoKqX1Sy1xqlqntchR2vwtu82wt0XLsCMUtsJ0x2K5fFvEiaouWbViFg',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJIJE5jTu_BNqZ_LR4E82Kj6hBC7UlZ1BDeqhJEKDtQ-vTzpwv8m3Rj16kJtE2dSBAg',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJBi8YFt3WToEbW0L6-BmqV4_HjXOvB0Z5IJ3GLVtBj8hcRP_O4mf9Ia6T8ITbfkKBw',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJFIvGogPbbDumfRVNxlgPRqYwmSTuhsE2OyzcnJXRc49-6SanEHKsiPM-vIlUlGOFQ',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJNt8AklXWChR3D1545zHosDzkcT6gNUv9UwevKbgsGE6_Skgr9Et86OA8tw3PgW0ew',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJMNA5xeVo8u0lFyyFwLvexHmQTRi_oaiYuS6wCrZHm3931x3KbVeeLJv8hI2YvL1yQ',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJD3V9rkvi7OQ852nZavb3k2sdVOl_JEjqRbhYBhgPPqhgaIkawklFj-4w5oQ9BR1xg',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJDLPwxnddCKwDrDOjA9lWQkB1_2KcFc9L48-AjNV7lMTDV7EvpwWRy3aLVmvXmmi-w',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJNMTtzowAlaXSt-ZZajhbZeYcZj9njs9Czy2iRLoh6m-PrGRaCb7koaoeVzHvMwrFg',\n      'https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJMQV_yLC-b3ewPS_sYPnmdwvIlB-IlyEjoyVtv13rE7Qulx6GR2H-p5JIIZNDWXg6Q',\n      'https://res.wx.qq.com/op_res/SPJ17foR6CQ_1kS3N3iWEozVgUwwyXBMXhm7WFzcvPMGQH8Nqoh1jLUayawk0flboyz1IDNkU3foqBvNIgI11Q',\n    ]\n    const now = Date.now()\n    srcList.forEach((src, index) => {\n      const id = (now + index).toString()\n      imageList.push({\n        id,\n        src,\n        thumbnail: thumbnailList[index],\n        createTime: now - (index * 3600 * 1000)\n      })\n    })\n  }\n\n  return imageList\n}\n"
  },
  {
    "path": "examples/album/utils/worklet.js",
    "content": "export function getShared(renderer) {\n  if (renderer === 'skyline') return wx.worklet.shared\n  else return val => ({ value: val })\n}\n\nexport function getTiming(renderer) {\n  'worklet'\n  if (renderer === 'skyline') return wx.worklet.timing\n  else return (target, options, callback) => {\n    if (typeof callback === 'function') setTimeout(callback, 0)\n    return target\n  }\n}\n\nexport function getRunOnUI(renderer) {\n  if (renderer === 'skyline') return wx.worklet.runOnUI\n  else return func => func\n}\n\nexport function getRunOnJS(renderer) {\n  'worklet'\n  if (renderer === 'skyline') return wx.worklet.runOnJS\n  else return func => func\n}\n"
  },
  {
    "path": "examples/app-bar/.eslintrc.js",
    "content": "/*\n * Eslint config file\n * Documentation: https://eslint.org/docs/user-guide/configuring/\n * Install the Eslint extension before using this feature.\n */\nmodule.exports = {\n  env: {\n    es6: true,\n    browser: true,\n    node: true,\n  },\n  ecmaFeatures: {\n    modules: true,\n  },\n  parserOptions: {\n    ecmaVersion: 2018,\n    sourceType: 'module',\n  },\n  globals: {\n    wx: true,\n    App: true,\n    Page: true,\n    getCurrentPages: true,\n    getApp: true,\n    Component: true,\n    requirePlugin: true,\n    requireMiniProgram: true,\n  },\n  // extends: 'eslint:recommended',\n  rules: {},\n}\n"
  },
  {
    "path": "examples/app-bar/app-bar/index.js",
    "content": "// components/app-bar/index.js\n\nconst { shared, timing, Easing } = wx.worklet\n\nexport const GestureState = {\n  POSSIBLE: 0,\n  BEGIN: 1,\n  ACTIVE: 2,\n  END: 3,\n  CANCELLED: 4,\n}\n\nexport const lerp = function (begin, end, t) {\n  'worklet'\n  return begin + (end - begin) * t\n}\n\nexport const clamp = function (cur, lowerBound, upperBound) {\n  'worklet'\n  if (cur > upperBound) return upperBound\n  if (cur < lowerBound) return lowerBound\n  return cur\n}\n\nconst systemInfo = wx.getSystemInfoSync()\nconst { statusBarHeight, screenHeight, screenWidth, safeArea } = systemInfo\nconsole.info('@@@ systemInfo', systemInfo)\nComponent({\n  properties: {\n\n  },\n\n  data: {\n    maxCoverSize: 0,\n    statusBarHeight: 0,\n    musicCover: 'https://res.wx.qq.com/op_res/Nu9XXzXcXnD1j5EgWQ2ElxNcl1yMvnKypRo4MTbjOv7FC3saigGoOBTZibyESC7EXaClnPYhB6pvfb-IRmso6g'\n  },\n\n  lifetimes: {\n    attached() {\n      const progress = shared(0)\n      const initCoverSize = 60 // 初始图片大小\n      const pagePadding = 24\n      const maxCoverSize = screenWidth - 2 * pagePadding\n      const safeAreaInsetBottom = screenHeight - safeArea.bottom\n      const isIOS = systemInfo.system.indexOf('iOS') >= 0\n      this.setData({ statusBarHeight, maxCoverSize })\n\n      this.applyAnimatedStyle('.cover', () => {\n        'worklet'\n        const height = initCoverSize + (maxCoverSize - initCoverSize) * progress.value\n        return {\n          width: `${height}px`,\n          height:`${height}px`,\n        }\n      })\n\n      this.applyAnimatedStyle('.expand-container', () => {\n        'worklet'\n        const t = progress.value\n        const maxRadius = 30\n        const radius = isIOS ? maxRadius * t : 0\n        const initBarHeight = initCoverSize + 8 * 2 + safeAreaInsetBottom\n        return {\n          top: `${(screenHeight - initBarHeight) * (1 - t)}px`,\n          borderRadius: `${radius}px ${radius}px 0px 0px`\n        }\n      })\n\n      this.applyAnimatedStyle('.title-wrap', () => {\n        'worklet'\n        return {\n          opacity: 1 - progress.value\n        }\n      })\n\n      const navBarHeight = statusBarHeight + (isIOS ? 40 : 44)\n      this.applyAnimatedStyle('.nav-bar', () => {\n        'worklet'\n        const t = progress.value\n        const threshold = 0.8\n        const opacity = t < threshold ? 0 : (t - threshold) / (1 - threshold)\n\n        return {\n          opacity,\n          height: `${navBarHeight * progress.value}px`\n        }\n      })\n\n      this.progress = progress\n    }\n  },\n\n  methods: {\n    close() {\n      this.progress.value = timing(0, {\n        duration: 250,\n        easing: Easing.ease\n      })\n    },\n\n    expand() {\n      this.progress.value = timing(1, {\n        duration: 250,\n        easing: Easing.ease\n      })\n    },\n\n    handleDragUpdate(delta) {\n      'worklet'\n      const curValue = this.progress.value\n      const newVal = curValue - delta\n      this.progress.value = clamp(newVal, 0.0, 1.0)\n    },\n\n    handleDragEnd(velocity) {\n      'worklet'\n      const t = this.progress.value\n      let animateForward = false\n      if (Math.abs(velocity) >= 1) {\n        animateForward = velocity <= 0\n      } else {\n        animateForward = t > 0.7\n      }\n      const animationCurve = Easing.out(Easing.ease)\n      if (animateForward) {\n        this.progress.value = timing(\n          1.0, {\n          duration: 200,\n          easing: animationCurve,\n        })\n      } else {\n        this.progress.value = timing(\n          0.0, {\n          duration: 250,\n          easing: animationCurve,\n        })\n      }\n    },\n\n    handleVerticalDrag(evt) {\n      'worklet'\n      if (evt.state === GestureState.ACTIVE) {\n        const delta = evt.deltaY / screenHeight\n        this.handleDragUpdate(delta)\n      } else if (evt.state === GestureState.END) {\n        const velocity = evt.velocityY / screenHeight\n        this.handleDragEnd(velocity)\n      } else if (evt.state === GestureState.CANCELLED) {\n        this.handleDragEnd(0.0)\n      }\n    },\n  },\n\n})"
  },
  {
    "path": "examples/app-bar/app-bar/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {}\n}"
  },
  {
    "path": "examples/app-bar/app-bar/index.wxml",
    "content": "<!-- components/app-bar/index.wxml -->\n<vertical-drag-gesture-handler worklet:ongesture=\"handleVerticalDrag\">\n\t<view class=\"expand-container\">\n\t\t<!-- 放大模式：nav-bar -->\n\t\t<view class=\"nav-bar column\">\n\t\t\t<view style=\"height: {{statusBarHeight}}px;\" />\n\t\t\t<view style=\"flex: 1;\" class=\"column-main-center\">\n\t\t\t\t<image\n\t\t\t\t class=\"icon--back\"\n\t\t\t\t mode=\"aspectFill\"\n\t\t\t\t src=\"/assets/arrow-down.png\"\n\t\t\t\t bind:tap=\"close\"\n\t\t\t\t/>\n\t\t\t</view>\n\t\t</view>\n\n\t\t<!-- 跟着手势变化 -->\n\t\t<view class=\"cover-area\" style=\"height: {{maxCoverSize}}px;\">\n\t\t\t<view class=\"row \" bind:tap=\"expand\">\n\t\t\t\t<image class=\"cover\" mode=\"aspectFill\" src=\"{{musicCover}}\" />\n\t\t\t\t<view class=\"title-wrap row-between\">\n\t\t\t\t\t<view class=\"title column\">\n\t\t\t\t\t\t<text overflow=\"ellipsis\" max-lines=\"1\">Skyline 渲染框架入门与实践</text>\n\t\t\t\t\t\t<text class=\"name\" overflow=\"ellipsis\" max-lines=\"1\">小程序技术专员 - binnie</text>\n\t\t\t\t\t</view>\n\t\t\t\t\t<view class=\"row\">\n\t\t\t\t\t\t<image class=\"icon\" style=\"margin-right: 24px;\" src=\"/assets/play.png\" />\n\t\t\t\t\t\t<image class=\"icon\" src=\"/assets/next.png\" />\n\t\t\t\t\t</view>\n\t\t\t\t</view>\n\t\t\t</view>\n\t\t</view>\n\n\t\t<!-- 放大模式：小字 -->\n\t\t<view class=\"row-between\">\n\t\t\t<text>微信学堂</text>\n\t\t\t<text>88 人在学</text>\n\t\t</view>\n\n\t\t<!-- 放大模式：标题 -->\n\t\t<view class=\"music-title column\" style=\"margin-top: 50px;\">\n\t\t\t<text>Skyline 渲染框架入门与实践</text>\n\t\t\t<text class=\"name\">小程序技术专员 - binnie</text>\n\t\t</view>\n\n\t\t<!-- 底部操作栏 -->\n\t\t<view class=\"footer row-between\" style=\"margin-top: 50px;\">\n\t\t\t<image class=\"icon\" src=\"/assets/next.png\" style=\"transform: rotate(180deg);\" />\n\t\t\t<image class=\"icon\" src=\"/assets/play.png\" />\n\t\t\t<image class=\"icon\" src=\"/assets/next.png\" />\n\t\t</view>\n\t</view>\n</vertical-drag-gesture-handler>\n\n"
  },
  {
    "path": "examples/app-bar/app-bar/index.wxss",
    "content": ".expand-container {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  padding: 0 24px;\n  padding-bottom: env(safe-area-inset-bottom);\n  pointer-events: auto;\n  overflow: hidden;\n  background-color: #e9f8f7;\n  color: #7e8081;\n}\n\n.hide {\n  display: none;\n}\n\n.nav-bar {\n  overflow: hidden;\n  box-sizing: border-box;\n}\n\n.icon--back {\n  width: 30px;\n  height: 30px;\n}\n\n.title {\n  margin-left: 10px;\n  min-width: 160px;\n  flex: 1;\n}\n.title .name {\n  margin-top: 4px;\n  font-size: 12px;\n}\n\n.title-wrap {\n  flex: 1;\n  min-width: 240px;\n}\n\n.footer {\n  padding: 0 24px;\n}\n\n.footer .icon {\n  width: 40px;\n  height: 40px;\n}\n\n.expand-cover {\n  width: 100%;\n  height: 100%;\n}\n\n.music-title {\n  margin-top: 48px;\n  font-size: 20px;\n  font-weight: bold;\n  color: #07c160;\n}\n.music-title .name {\n  margin-top: 12px;\n  font-size: 14px;\n  font-weight: 200;\n  color: #b1b2b3;\n}\n\n.cover-area {\n  margin: 8px 0;\n  width: 100%;\n  aspect-ratio: 1 / 1;\n  overflow: hidden;\n}\n\n.cover {\n  /* aspect-ratio: 1 / 1; */\n  flex-shrink: 0;\n}\n\n.icon {\n  width: 18px;\n  height: 18px;\n}\n\n.row {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n}\n\n.row-between {\n  display: flex;\n  flex-direction: row;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.column {\n  display: flex;\n  flex-direction: column;\n}\n\n.column-main-center {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n}\n\n.column-cross-center {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n\n.center {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.circle {\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n  border-radius: 50%;\n}"
  },
  {
    "path": "examples/app-bar/app.js",
    "content": "// app.js\nApp({})\n"
  },
  {
    "path": "examples/app-bar/app.json",
    "content": "{\n  \"pages\": [\n    \"pages/index/index\",\n    \"pages/detail/index\"\n  ],\n  \"window\": {\n    \"navigationBarTextStyle\": \"black\",\n    \"navigationStyle\": \"custom\"\n  },\n  \"style\": \"v2\",\n  \"renderer\": \"skyline\",\n  \"appBar\": {},\n  \"rendererOptions\": {\n    \"skyline\": {\n      \"defaultDisplayBlock\": true,\n      \"defaultContentBox\": true,\n      \"disableABTest\": true,\n      \"sdkVersionBegin\": \"3.0.0\",\n      \"sdkVersionEnd\": \"15.255.255\"\n    }\n  },\n  \"componentFramework\": \"glass-easel\",\n  \"sitemapLocation\": \"sitemap.json\",\n  \"lazyCodeLoading\": \"requiredComponents\"\n}"
  },
  {
    "path": "examples/app-bar/app.wxss",
    "content": "/**app.wxss**/\n.container {\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: space-between;\n  padding: 200rpx 0;\n  box-sizing: border-box;\n} \n"
  },
  {
    "path": "examples/app-bar/components/navigation-bar/navigation-bar.js",
    "content": "Component({\n  options: {\n      multipleSlots: true // 在组件定义时的选项中启用多slot支持\n  },\n  /**\n   * 组件的属性列表\n   */\n  properties: {\n      extClass: {\n          type: String,\n          value: ''\n      },\n      title: {\n          type: String,\n          value: ''\n      },\n      background: {\n          type: String,\n          value: ''\n      },\n      color: {\n          type: String,\n          value: ''\n      },\n      back: {\n          type: Boolean,\n          value: true\n      },\n      loading: {\n          type: Boolean,\n          value: false\n      },\n      animated: {\n          // 显示隐藏的时候opacity动画效果\n          type: Boolean,\n          value: true\n      },\n      show: {\n          // 显示隐藏导航，隐藏的时候navigation-bar的高度占位还在\n          type: Boolean,\n          value: true,\n          observer: '_showChange'\n      },\n      // back为true的时候，返回的页面深度\n      delta: {\n          type: Number,\n          value: 1\n      }\n  },\n  /**\n   * 组件的初始数据\n   */\n  data: {\n      displayStyle: ''\n  },\n  attached() {\n    const isSupport = !!wx.getMenuButtonBoundingClientRect\n    const rect = wx.getMenuButtonBoundingClientRect\n        ? wx.getMenuButtonBoundingClientRect()\n        : null\n    wx.getSystemInfo({\n        success: (res) => {\n            const ios = !!(res.system.toLowerCase().search('ios') + 1)\n            this.setData({\n                ios,\n                statusBarHeight: res.statusBarHeight,\n                navBarHeight: rect.bottom - rect.top + 10,\n                innerWidth: isSupport ? `width:${rect.left}px` : '',\n                innerPaddingRight: isSupport\n                    ? `padding-right:${res.windowWidth - rect.left}px`\n                    : '',\n                leftWidth: isSupport ? `width:${res.windowWidth - rect.left}px` : '',\n                theme: res.theme || 'light',\n            })\n        }\n    })\n\n    if (wx.onThemeChange) {\n      wx.onThemeChange(({theme}) => {\n        this.setData({theme})\n      })\n    }\n  },\n  detached() {\n    if (wx.offThemeChange) {\n      wx.offThemeChange()\n    }\n  },\n  /**\n   * 组件的方法列表\n   */\n  methods: {\n      _showChange(show) {\n          const animated = this.data.animated\n          let displayStyle = ''\n          if (animated) {\n              displayStyle = `opacity: ${\n                  show ? '1' : '0'\n              };-webkit-transition:opacity 0.5s;transition:opacity 0.5s;`\n          } else {\n              displayStyle = `display: ${show ? '' : 'none'}`\n          }\n          this.setData({\n              displayStyle\n          })\n      },\n      back() {\n          const data = this.data\n          console.log('---------222',getCurrentPages().length)\n          if (data.delta) {\n              wx.navigateBack({\n                  delta: data.delta\n              })\n          }\n          // 如果是直接打开的，就默认回首页\n          if (getCurrentPages().length == 1) {\n            console.log('---------333')\n            wx.switchTab({\n              url: '/page/component/index',\n              complete: (res) => {\n                console.log(res)\n              }\n            })\n          }\n          this.triggerEvent('back', { delta: data.delta }, {})\n      }\n  }\n})\n"
  },
  {
    "path": "examples/app-bar/components/navigation-bar/navigation-bar.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {},\n  \"componentFramework\": \"glass-easel\",\n  \"renderer\": \"skyline\",\n  \"styleIsolation\": \"apply-shared\"\n}\n"
  },
  {
    "path": "examples/app-bar/components/navigation-bar/navigation-bar.wxml",
    "content": "<view class=\"weui-navigation-bar {{extClass}}\" data-weui-theme=\"{{theme}}\">\n  <view class=\"weui-navigation-bar__inner\" style=\"padding-top: {{statusBarHeight}}px; height: {{navBarHeight}}px; color: {{color}}; background: {{background}}; {{displayStyle}}; {{innerPaddingRight}};\">\n    <view class='weui-navigation-bar__left' style=\"{{leftWidth}}\">\n      <block wx:if=\"{{back}}\">\n        <view class=\"weui-navigation-bar__buttons\">\n          <view\n            bindtap=\"back\"\n            class=\"weui-navigation-bar__btn_goback_wrapper\"\n            hover-class=\"weui-active\"\n            aria-role=\"button\"\n            aria-label=\"返回\"\n          >\n            <view class=\"weui-navigation-bar__button weui-navigation-bar__btn_goback\"></view>\n          </view>\n        </view>\n      </block>\n      <block wx:else>\n        <slot name=\"left\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__center'>\n      <view wx:if=\"{{loading}}\" class=\"weui-navigation-bar__loading\" aria-role=\"alert\">\n        <view\n          class=\"weui-loading\"\n          style=\"width:{{size.width}}rpx;height:{{size.height}}rpx;\"\n          aria-role=\"img\"\n          aria-label=\"加载中\"\n        ></view>\n      </view>\n      <block wx:if=\"{{title}}\">\n        <text>{{title}}</text>\n      </block>\n      <block wx:else>\n        <slot name=\"center\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__right'>\n      <slot name=\"right\"></slot>\n    </view>\n  </view>\n</view>\n"
  },
  {
    "path": "examples/app-bar/components/navigation-bar/navigation-bar.wxss",
    "content": ".weui-navigation-bar {\n  display: flex;\n  overflow: hidden;\n  color: rgba(0, 0, 0, .9);\n  width: 100vw;\n}\n\n.weui-navigation-bar__placeholder {\n  background: #f7f7f7;\n  position: relative;\n}\n\n.weui-navigation-bar__inner, .weui-navigation-bar__inner .weui-navigation-bar__left {\n  display: flex;\n  align-items: center;\n  flex-direction: row;\n}\n\n.weui-navigation-bar__inner {\n  position: relative;\n  padding-right: 95px;\n  width: 100vw;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left {\n  position: relative;\n  width: 95px;\n  padding-left: 16px;\n}\n\n.weui-navigation-bar__btn_goback_wrapper {\n  padding: 11px 18px 11px 16px;\n  margin: -11px -18px -11px -16px;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left .weui-navigation-bar__btn_goback {\n  font-size: 12px;\n  width: 12px;\n  height: 24px;\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E\") no-repeat 50% 50%;\n  background-color: currentColor;\n  background-size: cover;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__center {\n  font-size: 17px;\n  text-align: center;\n  position: relative;\n  flex: 1;\n  display: flex; \n  align-items: center;\n  justify-content: center;\n  font-weight: bold;\n}\n\n[data-weui-theme=dark].weui-navigation-bar {\n  color: hsla(0, 0%, 100%, .8);\n}\n[data-weui-theme=dark] .weui-navigation-bar__inner {\n  background-color: #1f1f1f;\n}\n"
  },
  {
    "path": "examples/app-bar/pages/detail/index.js",
    "content": "// pages/detail/index.js\nconst musicList = [\n  {\n    id: 0,\n    coverImg: 'https://res.wx.qq.com/op_res/Nu9XXzXcXnD1j5EgWQ2ElxNcl1yMvnKypRo4MTbjOv7FC3saigGoOBTZibyESC7EXaClnPYhB6pvfb-IRmso6g',\n    title: 'Skyline 渲染框架'\n  },\n  {\n    id: 1,\n    coverImg: 'https://res.wx.qq.com/op_res/Nu9XXzXcXnD1j5EgWQ2El3JJ3FgQX_YP9sI6kJD_nLjnkdN19yZ5nLtS3cqtNUx621vrni0Kjy5uoX_QMlBJgQ',\n    title: '小程序性能优化'\n  },\n  {\n    id: 2,\n    coverImg: 'https://res.wx.qq.com/op_res/Nu9XXzXcXnD1j5EgWQ2ElwWbBogi5f0NNRBkuJWfE8HQzysKxBaoCJ-YBr7irwn_uE37dHQTWcHK2uOHIWsQ3Q',\n    title: '医疗行业实践'\n  },\n\n]\nPage({\n  data: {\n    music: musicList[0],\n    albumMusicList: [\n      {\n      id: 0,\n      coverImg: 'https://res.wx.qq.com/op_res/Nu9XXzXcXnD1j5EgWQ2ElxNcl1yMvnKypRo4MTbjOv7FC3saigGoOBTZibyESC7EXaClnPYhB6pvfb-IRmso6g',\n      name: 'Skyline 渲染框架',\n      author: '小程序技术专员 - binnie'\n    },\n    {\n      id: 1,\n      coverImg: 'https://res.wx.qq.com/op_res/Nu9XXzXcXnD1j5EgWQ2El3JJ3FgQX_YP9sI6kJD_nLjnkdN19yZ5nLtS3cqtNUx621vrni0Kjy5uoX_QMlBJgQ',\n      name: '小程序性能优化',\n      author: '小程序性能优化专家'\n    },\n    {\n      id: 2,\n      coverImg: 'https://res.wx.qq.com/op_res/Nu9XXzXcXnD1j5EgWQ2ElwWbBogi5f0NNRBkuJWfE8HQzysKxBaoCJ-YBr7irwn_uE37dHQTWcHK2uOHIWsQ3Q',\n      name: '医疗行业实践',\n      author: '小程序医疗行业专家'\n    },\n    ]\n  },\n\n  onLoad(query) {\n    const idx = query.idx\n    if (idx) {\n      this.setData({\n        music: musicList[idx]\n      })\n    }\n  },\n\n  onReady() {\n\n  },\n\n\n  onShow() {\n\n  },\n})"
  },
  {
    "path": "examples/app-bar/pages/detail/index.json",
    "content": "{\n  \"usingComponents\": {\n    \"navigation-bar\": \"../../components/navigation-bar/navigation-bar\"\n  }\n}"
  },
  {
    "path": "examples/app-bar/pages/detail/index.wxml",
    "content": "<navigation-bar\n title=\"微信学堂\"\n back=\"{{true}}\"\n color=\"black\"\n background=\"#FFF\"\n/>\n<scroll-view\n class=\"scroll-area\"\n type=\"list\"\n scroll-y\n show-scrollbar=\"{{false}}\"\n>\n\t<view class=\"cover-wrap center\">\n\t\t<image class=\"cover\" src=\"{{music.coverImg}}\" mode=\"aspectFill\" />\n\t</view>\n\n\t<view style=\"margin: 60px 0 24px;\">为你推荐</view>\n\t<view class=\"album-music row\" wx:for=\"{{albumMusicList}}\" wx:key=\"id\">\n\t\t<image class=\"album-music-cover\" src=\"{{item.coverImg}}\" mode=\"aspectFill\" />\n\t\t<view class=\"column\">\n\t\t\t<view>{{item.name}}</view>\n\t\t\t<view class=\"author\">{{item.author}}</view>\n\t\t</view>\n\t</view>\n</scroll-view>\n\n"
  },
  {
    "path": "examples/app-bar/pages/detail/index.wxss",
    "content": "page {\n  display: flex;\n  flex-direction: column;\n  height: 100vh;\n}\n\n.scroll-area {\n  flex: 1;\n  overflow-y: hidden;\n  padding: 0 24px;\n  box-sizing: border-box;\n  margin-bottom: calc(84px + env(safe-area-inset-bottom));\n}\n\n.intro {\n  padding: 30px;\n  text-align: center;\n}\n\n.cover {\n  width: 250px;\n  height: 250px;\n}\n\n.album-music {\n  padding: 16px 0;\n  border-top: 0.5px solid #f1e9e9;\n}\n\n.album-music-cover {\n  width: 48px;\n  height: 48px;\n  margin-right: 16px;\n}\n\n.row {\n  display: flex;\n  flex-direction: row;\n}\n\n.row-between {\n  display: flex;\n  flex-direction: row;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.column {\n  display: flex;\n  flex-direction: column;\n}\n\n.column-main-center {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n}\n\n.column-cross-center {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n\n.center {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.circle {\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n  border-radius: 50%;\n}\n.author {\n  font-size: 12px;\n  color: #7e8081;\n  margin-top: 6px;\n}\n"
  },
  {
    "path": "examples/app-bar/pages/index/index.js",
    "content": "const app = getApp()\n\nPage({\n  data: {\n    back: false,\n    maxCoverSize: 0,\n    musicList: [\n      {\n        id: 0,\n        coverImg: 'https://res.wx.qq.com/op_res/Nu9XXzXcXnD1j5EgWQ2ElxNcl1yMvnKypRo4MTbjOv7FC3saigGoOBTZibyESC7EXaClnPYhB6pvfb-IRmso6g',\n        title: 'Skyline 渲染框架'\n      },\n      {\n        id: 1,\n        coverImg: 'https://res.wx.qq.com/op_res/Nu9XXzXcXnD1j5EgWQ2El3JJ3FgQX_YP9sI6kJD_nLjnkdN19yZ5nLtS3cqtNUx621vrni0Kjy5uoX_QMlBJgQ',\n        title: '小程序性能优化'\n      },\n      {\n        id: 2,\n        coverImg: 'https://res.wx.qq.com/op_res/Nu9XXzXcXnD1j5EgWQ2ElwWbBogi5f0NNRBkuJWfE8HQzysKxBaoCJ-YBr7irwn_uE37dHQTWcHK2uOHIWsQ3Q',\n        title: '医疗行业实践'\n      },\n\n    ]\n  },\n  onLoad() {\n    // 在小程序示例中展示返回按钮\n    if (this.route.includes('packageSkylineExamples/examples/')) {\n      this.setData({\n        back: true\n      })\n    }\n  },\n  onShow() {\n    // 仅在 app-bar demo 页面展示\n    if (typeof this.getAppBar === 'function' ) {\n      const appBarComp = this.getAppBar()\n      // component.getAppBar 在 Skyline 中返回 appBar 组件实例，在 webview 中返回 null\n      if (appBarComp !== null) {\n        appBarComp.setData({\n          showAppbar: true\n        })\n      }\n    }\n  },\n\n  goDetail(e) {\n    const idx = e.currentTarget.dataset.idx\n    wx.navigateTo({\n      url: `../detail/index?idx=${idx}`,\n    })\n  }\n})\n"
  },
  {
    "path": "examples/app-bar/pages/index/index.json",
    "content": "{\n  \"usingComponents\": {\n    \"navigation-bar\": \"../../components/navigation-bar/navigation-bar\"\n  }\n}"
  },
  {
    "path": "examples/app-bar/pages/index/index.wxml",
    "content": "<navigation-bar\n title=\"微信学堂\"\n back=\"{{back}}\"\n color=\"black\"\n background=\"#FFF\"\n/>\n<scroll-view\n class=\"scroll-area\"\n type=\"list\"\n scroll-y\n show-scrollbar=\"{{false}}\"\n>\n\t<view class=\"title\">本月课程上新</view>\n\t<view class=\"cards\">\n\t\t<view\n\t\t class=\"card\"\n\t\t wx:for=\"{{musicList}}\"\n     wx:key=\"id\"\n     data-idx=\"{{index}}\"\n\t\t bind:tap=\"goDetail\"\n\t\t>\n\t\t\t<image class=\"cover\" mode=\"aspectFill\" src=\"{{item.coverImg}}\" />\n\t\t\t<view>{{item.title}}</view>\n\t\t</view>\n\t</view>\n\t<view class=\"title\">最近学过</view>\n\t<view class=\"cards\">\n\t\t<view\n\t\t class=\"card\"\n\t\t wx:for=\"{{musicList}}\"\n     wx:key=\"id\"\n     data-idx=\"{{index}}\"\n\t\t bind:tap=\"goDetail\"\n\t\t>\n\t\t\t<image class=\"cover\" mode=\"aspectFill\" src=\"{{item.coverImg}}\" />\n\t\t\t<view>{{item.title}}</view>\n\t\t</view>\n\t</view>\n</scroll-view>\n\n"
  },
  {
    "path": "examples/app-bar/pages/index/index.wxss",
    "content": "page {\n  display: flex;\n  flex-direction: column;\n  height: 100vh;\n}\n\n.scroll-area {\n  flex: 1;\n  overflow-y: hidden;\n  padding: 0 8px;\n  box-sizing: border-box;\n  margin-bottom: calc(84px + env(safe-area-inset-bottom));\n}\n\n.intro {\n  padding: 30px;\n  text-align: center;\n}\n\n.app-bar {\n  position: absolute;\n  width: 100vw;\n  height: 100vh;\n  pointer-events: none;\n}\n\n.title {\n  margin-top: 16px;\n  margin-left: 8px;\n  font-size: 20px;\n  font-weight: 600;\n}\n\n.cards {\n  /* margin: 24px 0; */\n  display: flex;\n  flex-direction: row;\n  flex-wrap: wrap;\n}\n\n.card {\n  display: flex;\n  justify-content: center;\n  flex-direction: column;\n  margin: 8px;\n}\n\n.cover {\n  width: 43vw;\n  height: 43vw;\n  margin-bottom: 10px;\n}"
  },
  {
    "path": "examples/app-bar/project.config.json",
    "content": "{\n    \"appid\": \"wxe5f52902cf4de896\",\n    \"compileType\": \"miniprogram\",\n    \"libVersion\": \"3.3.2\",\n    \"packOptions\": {\n        \"ignore\": [],\n        \"include\": []\n    },\n    \"setting\": {\n        \"coverView\": true,\n        \"es6\": true,\n        \"postcss\": true,\n        \"minified\": true,\n        \"enhance\": true,\n        \"showShadowRootInWxmlPanel\": true,\n        \"packNpmRelationList\": [],\n        \"babelSetting\": {\n            \"ignore\": [],\n            \"disablePlugins\": [],\n            \"outputPath\": \"\"\n        },\n        \"compileWorklet\": true\n    },\n    \"condition\": {},\n    \"editorSetting\": {\n        \"tabIndent\": \"auto\",\n        \"tabSize\": 4\n    }\n}"
  },
  {
    "path": "examples/app-bar/project.private.config.json",
    "content": "{\n    \"description\": \"项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档：https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html\",\n    \"projectname\": \"app-bar\",\n    \"setting\": {\n        \"compileHotReLoad\": true,\n        \"skylineRenderEnable\": true\n    },\n    \"libVersion\": \"3.3.3\"\n}"
  },
  {
    "path": "examples/app-bar/sitemap.json",
    "content": "{\n    \"desc\": \"关于本文件的更多信息，请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html\",\n    \"rules\": [{\n    \"action\": \"allow\",\n    \"page\": \"*\"\n    }]\n}"
  },
  {
    "path": "examples/associated-scroll-view/.eslintrc.js",
    "content": "/*\n * Eslint config file\n * Documentation: https://eslint.org/docs/user-guide/configuring/\n * Install the Eslint extension before using this feature.\n */\nmodule.exports = {\n  env: {\n    es6: true,\n    browser: true,\n    node: true,\n  },\n  ecmaFeatures: {\n    modules: true,\n  },\n  parserOptions: {\n    ecmaVersion: 2018,\n    sourceType: 'module',\n  },\n  globals: {\n    wx: true,\n    App: true,\n    Page: true,\n    getCurrentPages: true,\n    getApp: true,\n    Component: true,\n    requirePlugin: true,\n    requireMiniProgram: true,\n  },\n  // extends: 'eslint:recommended',\n  rules: {},\n}\n"
  },
  {
    "path": "examples/associated-scroll-view/app.js",
    "content": "// app.js\nApp({})\n"
  },
  {
    "path": "examples/associated-scroll-view/app.json",
    "content": "{\n  \"pages\": [\n    \"pages/index/index\"\n  ],\n  \"window\": {\n    \"backgroundTextStyle\": \"light\",\n    \"navigationBarBackgroundColor\": \"#fff\",\n    \"navigationBarTitleText\": \"Weixin\",\n    \"navigationBarTextStyle\": \"black\"\n  },\n  \"style\": \"v2\",\n  \"lazyCodeLoading\": \"requiredComponents\",\n  \"renderer\": \"skyline\",\n  \"rendererOptions\": {\n    \"skyline\": {\n      \"defaultDisplayBlock\": true,\n      \"disableABTest\": true,\n      \"sdkVersionBegin\": \"3.0.0\",\n      \"sdkVersionEnd\": \"15.255.255\"\n    }\n  },\n  \"componentFramework\": \"glass-easel\",\n  \"sitemapLocation\": \"sitemap.json\"\n}\n"
  },
  {
    "path": "examples/associated-scroll-view/app.wxss",
    "content": "/**app.wxss**/\n.container {\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: space-between;\n  padding: 200rpx 0;\n  box-sizing: border-box;\n} \n"
  },
  {
    "path": "examples/associated-scroll-view/components/category-list/category-list.js",
    "content": "const { shared } = wx.worklet\n\nconst GestureState = {\n  POSSIBLE: 0, // 0 此时手势未识别，如 panDown等\n  BEGIN: 1, // 1 手势已识别\n  ACTIVE: 2, // 2 连续手势活跃状态\n  END: 3, // 3 手势终止\n  CANCELLED: 4, // 4 手势取消，\n}\n\nComponent({\n  options: {\n    virtualHost: true,\n  },\n  properties: {\n    products: {\n      type: Object,\n      value: [],\n    },\n    max: {\n      type: Number,\n      value: 0,\n    },\n    index: {\n      type: Number,\n      value: 0,\n    },\n  },\n  lifetimes: {\n    created() {\n      this._swiping = shared(false)\n      this._canSwipe = shared(false)\n      this._scrollTop = shared(0)\n      this._scrollHeight = shared(100)\n      this._height = shared(0)\n    },\n    attached() {\n      this.createSelectorQuery().select('.product-list').boundingClientRect(rect => {\n        this._height.value = rect.height\n        this._scrollHeight.value = rect.height + 1 // 可滚动高度总是比滚动区域高一点\n      }).exec()\n    },\n  },\n  methods: {\n    getCanSwipe() {\n      return this._canSwipe\n    },\n    setSwipingValue(swiping) {\n      this._swiping = swiping\n    },\n    shouldScrollViewResp(e) {\n      'worklet'\n      // 前者是判断到顶往下拉，后者反之\n      this._canSwipe.value = this._scrollTop.value <= 0 && e.deltaY > 0 && this.properties.index !== 0 ||\n         this._scrollTop.value + this._height.value >= this._scrollHeight.value && e.deltaY < 0 && this.properties.index !== this.properties.max\n      // 滑动 swiper 期间 scroll-view 不可滚动\n      return !this._swiping.value && !this._canSwipe.value\n    },\n    handleScroll(e) {\n      'worklet'\n      this._scrollTop.value = e.detail.scrollTop\n      this._scrollHeight.value = e.detail.scrollHeight\n    },\n  },\n})\n"
  },
  {
    "path": "examples/associated-scroll-view/components/category-list/category-list.json",
    "content": "{\n    \"component\": true,\n    \"usingComponents\": {}\n}"
  },
  {
    "path": "examples/associated-scroll-view/components/category-list/category-list.wxml",
    "content": "<vertical-drag-gesture-handler\n tag=\"scroll-view-{{index}}\"\n native-view=\"scroll-view\"\n simultaneous-handlers=\"{{['swiper']}}\"\n worklet:should-response-on-move=\"shouldScrollViewResp\"\n>\n  <scroll-view class=\"product-list\" type=\"list\" scroll-y worklet:onscrollupdate=\"handleScroll\">\n    <view class=\"product-item\" wx:for=\"{{products}}\" wx:key=\"id\">\n      <image class=\"product-image\" fade-in src=\"{{item.image}}\"></image>\n      <view class=\"product-info\">\n        <text class=\"product-name\" max-lines=\"2\">{{item.name}}</text>\n        <view class=\"product-comment\"><text>{{item.comment}}</text></view>\n        <view class=\"product-data\"><text>{{item.sales}}人学过</text><text>好评度100%</text></view>\n        <span class=\"product-price\">\n          <text style=\"font-size: 10px;\">¥</text>\n          <text style=\"font-size: 16px; font-weight: bold;\">{{item.price}}</text>\n          <text style=\"color: gray;\"> 起</text>\n        </span>\n      </view>\n      <view class=\"product-add-to-cart\">+</view>\n    </view>\n  </scroll-view>\n</vertical-drag-gesture-handler>\n"
  },
  {
    "path": "examples/associated-scroll-view/components/category-list/category-list.wxss",
    "content": ".product-list {\n  height: 100%;\n}\n.product-item {\n  display: flex;\n  flex-direction: row;\n  padding: 8px;\n}\n.product-item:first-child {\n  padding-top: 16px;\n}\n.product-item:last-child {\n  padding-bottom: 16px;\n  border-bottom: 1px solid #F9F9F9;\n}\n.product-image {\n  width: 133px;\n  height: 100px;\n  border-radius: 5px;\n}\n.product-info {\n  flex: 1;\n  padding: 0 8px;\n  font-size: 10px;\n}\n.product-info view, .product-info text {\n  margin-bottom: 3px;\n}\n.product-name {\n  font-size: 16px;\n}\n.product-comment {\n  display: flex;\n  flex-direction: row;\n}\n.product-comment text {\n  background-color: #EEE;\n  border-radius: 3px;\n  padding: 1px 3px;\n}\n.product-data {\n  display: flex;\n  flex-direction: row;\n  color: gray;\n}\n.product-data text {\n  margin-right: 20px;\n}\n.product-discount {\n  display: flex;\n  flex-direction: row;\n}\n.product-discount text {\n  color: red;\n  border: 0.3px solid red;\n  padding:  0 3px;\n  border-radius: 3px;\n}\n.product-price {\n  color: red;\n  vertical-align: baseline;\n}\n.product-add-to-cart {\n  position: absolute;\n  right: 12px;\n  bottom: 12px;\n  border-radius: 6px;\n  color: #fff;\n  background-color: #44b549;\n  width: 20px;\n  height: 20px;\n  line-height: 16px;\n  font-weight: 500;\n  text-align: center;\n}\n"
  },
  {
    "path": "examples/associated-scroll-view/pages/index/index.js",
    "content": "import { getCategories, getProducts } from \"../../util\"\n\nconst { shared } = wx.worklet\n\nComponent({\n  data: {\n    categories: getCategories(),\n    selected: 0,\n    products: getProducts(),\n  },\n  lifetimes: {\n    created() {\n      this._swiping = shared(false)\n      this._canSwipe = []\n      this._selected = shared(0)\n      this._lastIndex = shared(0)\n    },\n    attached() {\n      this._canSwipe = this.selectAllComponents('.category-list').map(comp => {\n        comp.setSwipingValue(this._swiping)\n        return comp.getCanSwipe()\n      })\n    },\n  },\n  methods: {\n    shouldSwiperResp() {\n      'worklet'\n      if (this._lastIndex.value !== this._selected.value) {\n        this._lastIndex.value = this._selected.value\n        // 每次切换 swiper item 时重置，优先给滚动\n        this._canSwipe[this._selected.value].value = false\n      }\n      return this._canSwipe[this._selected.value].value\n    },\n    onSwiperStart() {\n      'worklet'\n      this._swiping.value = true\n    },\n    onSwiperEnd() {\n      'worklet'\n      this._swiping.value = false\n    },\n    onChange(e) {\n      const {current} = e.detail\n      this.setData({\n        selected: current,\n      })\n      this._selected.value = current\n      wx.vibrateShort({\n        type: 'light',\n      })\n    },\n  },\n})\n"
  },
  {
    "path": "examples/associated-scroll-view/pages/index/index.json",
    "content": "{\n  \"usingComponents\": {\n    \"category-list\": \"../../components/category-list/category-list\"\n  },\n  \"disableScroll\": true,\n  \"navigationStyle\": \"custom\"\n}"
  },
  {
    "path": "examples/associated-scroll-view/pages/index/index.wxml",
    "content": "<!--index.wxml-->\n<view class=\"navigation-bar\">\n  <view class=\"navigation-bar-content black\">\n    <view class=\"back\">ㄑ</view>\n    <view class=\"search\"><view class=\"search-input\">请输入课程名称</view></view>\n    <view class=\"more\"></view>\n  </view>\n</view>\n<view class=\"first-category\">\n  <scroll-view class=\"first-category-list\"\n   scroll-x\n   type=\"list\"\n   show-scrollbar=\"{{false}}\"\n   enable-flex\n  >\n    <view class=\"first-category-item\" wx:for=\"{{categories}}\" wx:key=\"name\" list-item>\n      <image class=\"first-category-item-image\" mode=\"aspectFill\" src=\"{{item.image}}\"></image>\n      <view class=\"first-category-item-name\">{{item.name}}</view>\n    </view>\n  </scroll-view>\n</view>\n<view class=\"main\">\n  <view class=\"second-category\">\n    <view wx:for=\"{{categories}}\" wx:key=\"name\" class=\"second-category-item {{selected === index ? 'selected' : ''}}\">{{item.name}}</view>\n  </view>\n  <vertical-drag-gesture-handler\n   tag=\"swiper\"\n   native-view=\"swiper\"\n   simultaneous-handlers=\"{{['scroll-view-0', 'scroll-view-1']}}\"\n   worklet:should-response-on-move=\"shouldSwiperResp\"\n  >\n    <swiper class=\"product-list-wrapper\" vertical cache-extent=\"1\" bind:change=\"onChange\" worklet:onscrollstart=\"onSwiperStart\" worklet:onscrollend=\"onSwiperEnd\">\n      <swiper-item wx:for=\"{{categories}}\" wx:key=\"name\">\n        <category-list class=\"category-list\" products=\"{{products}}\" max=\"{{categories.length}}\" index=\"{{index}}\" />\n      </swiper-item>\n    </swiper>\n  </vertical-drag-gesture-handler>\n</view>\n"
  },
  {
    "path": "examples/associated-scroll-view/pages/index/index.wxss",
    "content": "view {\n  position: relative;\n  box-sizing: border-box;\n}\npage {\n  background-color: #F9F9F9;\n  display: flex;\n  flex-direction: column;\n  height: 100vh;\n}\n\n.navigation-bar {\n  width: 100%;\n  padding-top: calc(env(safe-area-inset-top) + 0.001px);\n}\n.navigation-bar-content {\n  height: 44px;\n  display: flex;\n  flex-direction: row;\n  justify-content: center;\n  align-items: center;\n  font-size: 20px;\n}\n.navigation-bar-content.white {\n  color: white;\n}\n.navigation-bar-content.black {\n  color: black;\n}\n.navigation-bar-content .back, .navigation-bar-content .more {\n  width: 44px;\n  text-align: center;\n}\n.navigation-bar-content .search {\n  flex: 1;\n  margin-right: 60px;\n  margin-top: 7px;\n  align-self: flex-start;\n}\n.navigation-bar-content .search-input {\n  position: absolute;\n  right: 0;\n  width: 100%;\n  height: 30px;\n  line-height: 30px;\n  padding-left: 20px;\n  border: 0.5px solid #44b549;\n  border-radius: 15px;\n  font-size: 12px;\n  background-color: #F8F8F8;\n  color: #AAA;\n}\n\n.first-category {\n  height: 100px;\n}\n.first-category-list {\n  height: 100%;\n  display: flex;\n  flex-direction: row;\n}\n.first-category-item {\n  padding: 10px;\n  flex-shrink: 0;\n}\n.first-category-item-image {\n  width: 50px;\n  height: 50px;\n  border-radius: 100%;\n  border: 2px solid white;\n}\n.first-category-item-name {\n  font-size: 12px;\n  text-align: center;\n  width: 50px;\n  margin-top: 6px;\n}\n\n.main {\n  flex: 1;\n  display: flex;\n  flex-direction: row;\n}\n.second-category {\n  font-size: 12px;\n  background-color: #F9F9F9;\n}\n.second-category-item {\n  padding: 15px 5px;\n  width: 80px;\n  transition: ease .3s;\n  transition-property: font-size;\n  text-align: center;\n}\n.second-category-item.selected {\n  background-color: white;\n  font-size: 14px;\n  font-weight: bold;\n  color: #44b549;\n}\n\n.product-list-wrapper {\n  flex: 1;\n  background-color: white;\n  height: auto;\n}\n"
  },
  {
    "path": "examples/associated-scroll-view/project.config.json",
    "content": "{\n    \"appid\": \"wxe5f52902cf4de896\",\n    \"compileType\": \"miniprogram\",\n    \"libVersion\": \"latest\",\n    \"packOptions\": {\n        \"ignore\": [],\n        \"include\": []\n    },\n    \"setting\": {\n        \"coverView\": true,\n        \"es6\": true,\n        \"postcss\": true,\n        \"minified\": true,\n        \"enhance\": true,\n        \"showShadowRootInWxmlPanel\": true,\n        \"packNpmRelationList\": [],\n        \"babelSetting\": {\n            \"ignore\": [],\n            \"disablePlugins\": [],\n            \"outputPath\": \"\"\n        },\n        \"condition\": false,\n        \"skylineRenderEnable\": true,\n        \"compileWorklet\": true\n    },\n    \"condition\": {},\n    \"editorSetting\": {\n        \"tabIndent\": \"auto\",\n        \"tabSize\": 4\n    }\n}"
  },
  {
    "path": "examples/associated-scroll-view/project.private.config.json",
    "content": "{\n    \"description\": \"项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档：https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html\",\n    \"projectname\": \"associated-scroll-view\",\n    \"setting\": {\n        \"compileHotReLoad\": false,\n        \"skylineRenderEnable\": true\n    },\n    \"libVersion\": \"latest\",\n    \"condition\": {}\n}"
  },
  {
    "path": "examples/associated-scroll-view/sitemap.json",
    "content": "{\n    \"desc\": \"关于本文件的更多信息，请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html\",\n    \"rules\": [{\n    \"action\": \"allow\",\n    \"page\": \"*\"\n    }]\n}"
  },
  {
    "path": "examples/associated-scroll-view/util.js",
    "content": "export function getCategories() {\n  const categories = [\n    '中小商户',\n    '商超零售',\n    '品牌服饰',\n    '餐饮',\n    '医疗',\n    '酒旅',\n    '政务',\n    '开发技术',\n    '产品能力',\n    '运营规范',\n  ]\n  const images = [\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU_LhYxhaP5JTy7TWgezsDY7RW_l_e04fR7oG7sCKmS8hc8mVeZaY6eUWT3nk-ww_ZQ',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU-O3axOjUJGFgutF9Xc1JL1uxXFWYdW85mWG0Zvm5nv7rvP18CJ0q6-RRFM0xWLLog',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU3ywQmrV-rSREDwo0Hp9m7iIZZ7Njvjq_TlOg_0ss0cgQL0pfKOuB2NRpAcwfALxvw',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU1GROxmiPIBOCoA5Es44GxjN0KuCQQsoxEH33l05TCgk04n0dssHAIPxIV2ycSlSJA',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU68wmBQzYcfQfuIAh1IKWq7OyG0EWxdWGhotYHFh-k-JpmkJ1Otq-mYUT8Dp3iucvg',\n    'https://res.wx.qq.com/op_res/UxKgRAAdvQE0sTh7eCEwT1ASo_fbE3TppPoza_9I7U7OreGNv8F8ltq80gqQSzxa-86bt-gWalLtgPDAhflj-w',\n    'https://res.wx.qq.com/op_res/UxKgRAAdvQE0sTh7eCEwT-SSHr1ULMcspj1yPw2dBQkxDV-Y_fOHodKNyHbS2JwBEVLnVKF2X_TPOhwZG9m0hQ',\n    'https://res.wx.qq.com/op_res/Zmvv0fisUjaMjuqWLhWWkuzGktaXJEQt46EaKsCKeT06Z4tROseXN0joI7h2qwzqyx2FUy57cveZL-8iArI8_Q',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJitcJIfp9P4VSWxi3126XeiyZ2BnnH0xg-oIXAUgHBgaHjBMwxzSjSkEkTMRqzlKZw',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJnw5zq4f_XW3swKAowexqAbziuojU5W9v4CJixA-NDJShkfS0ne3KWY_6SB56yqb3g',\n  ]\n  return categories.map((name, i) => {\n    return {name, image: images[i]}\n  })\n}\n\nexport function getProducts() {\n  let products = [\n    '小程序性能优化课程',\n    '小程序直播企业实践案例',\n    '微信客服轻松配置，入门必修',\n    '小程序如何帮助传统医院数字化？',\n    '帮你快速掌握小商店经营秘诀',\n    '了解小程序开发动态，听官方为你解读新能力',\n    '快速了解微信小程序在医疗行业的应用',\n    '解析常见小程序违规类型',\n    '想做互联网的生意，可以通过微信怎么经营呢？',\n    '政务行业小程序实践'\n  ]\n  let images = [\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU_LhYxhaP5JTy7TWgezsDY7RW_l_e04fR7oG7sCKmS8hc8mVeZaY6eUWT3nk-ww_ZQ',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU-O3axOjUJGFgutF9Xc1JL1uxXFWYdW85mWG0Zvm5nv7rvP18CJ0q6-RRFM0xWLLog',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU3ywQmrV-rSREDwo0Hp9m7iIZZ7Njvjq_TlOg_0ss0cgQL0pfKOuB2NRpAcwfALxvw',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU1GROxmiPIBOCoA5Es44GxjN0KuCQQsoxEH33l05TCgk04n0dssHAIPxIV2ycSlSJA',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU68wmBQzYcfQfuIAh1IKWq7OyG0EWxdWGhotYHFh-k-JpmkJ1Otq-mYUT8Dp3iucvg',\n    'https://res.wx.qq.com/op_res/UxKgRAAdvQE0sTh7eCEwT1ASo_fbE3TppPoza_9I7U7OreGNv8F8ltq80gqQSzxa-86bt-gWalLtgPDAhflj-w',\n    'https://res.wx.qq.com/op_res/UxKgRAAdvQE0sTh7eCEwT-SSHr1ULMcspj1yPw2dBQkxDV-Y_fOHodKNyHbS2JwBEVLnVKF2X_TPOhwZG9m0hQ',\n    'https://res.wx.qq.com/op_res/Zmvv0fisUjaMjuqWLhWWkuzGktaXJEQt46EaKsCKeT06Z4tROseXN0joI7h2qwzqyx2FUy57cveZL-8iArI8_Q',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJitcJIfp9P4VSWxi3126XeiyZ2BnnH0xg-oIXAUgHBgaHjBMwxzSjSkEkTMRqzlKZw',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJnw5zq4f_XW3swKAowexqAbziuojU5W9v4CJixA-NDJShkfS0ne3KWY_6SB56yqb3g',\n  ]\n  products = products.concat(products).map((name, id) => ({\n    id,\n    name,\n    image: images[(id % products.length)],\n    comment: '一如既往的好',\n    sales: 6500,\n    discount: 0.01,\n    price: 0.01\n  }))\n  return products\n}\n"
  },
  {
    "path": "examples/card_transition/app.js",
    "content": "App({})\n"
  },
  {
    "path": "examples/card_transition/app.json",
    "content": "{\n  \"pages\": [\n    \"pages/list/list\",\n    \"pages/detail/detail\"\n  ],\n  \"window\": {\n    \"backgroundTextStyle\": \"light\",\n    \"navigationBarBackgroundColor\": \"#fff\",\n    \"navigationBarTitleText\": \"Weixin\",\n    \"navigationBarTextStyle\": \"black\"\n  },\n  \"style\": \"v2\",\n  \"sitemapLocation\": \"sitemap.json\",\n  \"renderer\": \"skyline\",\n  \"rendererOptions\": {\n    \"skyline\": {\n      \"defaultDisplayBlock\": true,\n      \"disableABTest\": true,\n      \"sdkVersionBegin\": \"3.0.0\",\n      \"sdkVersionEnd\": \"15.255.255\"\n    }\n  },\n  \"componentFramework\": \"glass-easel\",\n  \"lazyCodeLoading\": \"requiredComponents\"\n}"
  },
  {
    "path": "examples/card_transition/app.wxss",
    "content": ""
  },
  {
    "path": "examples/card_transition/components/card/card.js",
    "content": "const { shared } = wx.worklet\n\nconst FlightDirection = {\n  PUSH: 0,\n  POP: 1,\n}\n\nComponent({\n  options: {\n    virtualHost: true,\n  },\n  properties: {\n    index: {\n      type: Number,\n      value: -1,\n    },\n    item: {\n      type: Object,\n      value: {},\n    },\n    cardWidth: {\n      type: Number,\n      value: 0\n    },\n  },\n  lifetimes: {\n    created() {\n      this.scale = shared(1)\n      this.opacity = shared(0)\n      this.direction = shared(0)\n      this.srcWidth = shared('100%')\n      this.radius = shared(5)\n\n      const beginRect = shared(undefined)\n      const endRect = shared(undefined)\n      wx.worklet.runOnUI(() => {\n        'worklet'\n        globalThis['RouteCardSrcRect'] = beginRect\n        globalThis['RouteCardDestRect'] = endRect\n      })()\n    },\n    attached() {\n      this.applyAnimatedStyle(\n        '.card_wrap', \n        () => {\n          'worklet'\n          return {\n            width: this.srcWidth.value,\n            transform: `scale(${this.scale.value})`,\n          }\n        }, \n        {\n          immediate: false,\n          flush: 'sync'\n        },\n        () => {}, \n      )\n\n      this.applyAnimatedStyle(\n        '.card_img',\n        () => {\n          'worklet'\n          return {\n            opacity: this.opacity.value,\n            borderTopRightRadius: this.radius.value, // 不带单位默认是 px\n            borderTopLeftRadius: this.radius.value,\n          }\n        },\n        {\n          immediate: false,\n          flush: 'sync'\n        },\n        () => {}, \n      )\n\n      this.applyAnimatedStyle(\n        '.card_desc',\n        () => {\n          'worklet'\n          return {\n            opacity: this.opacity.value,\n          }\n        },\n        {\n          immediate: false,\n          flush: 'sync'\n        },\n        () => {}, \n      )\n    },\n  },\n\n  methods: {\n    navigateTo(e) {\n      const { index, url, content, ratio, nickname } = e.currentTarget.dataset\n      const urlContent = `../../pages/detail/detail?index=${index}&url=${encodeURIComponent(url)}&content=${content}&ratio=${ratio}&nickname=${nickname}`\n      wx.navigateTo({\n        url: urlContent,\n        routeType: 'CardScaleTransition',\n      })\n    },\n    handleFrame(data) {\n      'worklet'\n      this.direction.value = data.direction\n      if (data.direction === FlightDirection.PUSH) { // 进入\n        // 飞跃过程中，卡片从 100% 改为固定宽度，通过 scale 手动控制缩放\n        this.srcWidth.value = `${data.begin.width}px`\n        this.scale.value = data.current.width / data.begin.width\n        this.opacity.value = 1 - data.progress\n        this.radius.value = 0\n        // this.shareImgHeight.value = data.begin.height\n\n      } else if (data.direction === FlightDirection.POP) { // 返回\n        this.scale.value = data.current.width / data.end.width\n        this.opacity.value = data.progress\n        this.radius.value = 5\n      }\n\n      // globalThis 是 UI 线程的全局变量，将 share-element 初始和目标尺寸保存起来，用于下一页面的缩放动画的计算\n      // TODO: 后续计划优化这里的接口设计\n      if (globalThis['RouteCardSrcRect'] && globalThis['RouteCardSrcRect'].value == undefined) {\n        globalThis['RouteCardSrcRect'].value = data.begin\n      }\n      if (globalThis['RouteCardDestRect'] && globalThis['RouteCardDestRect'].value == undefined) {\n        globalThis['RouteCardDestRect'].value = data.end\n      }\n    },\n  },\n})\n"
  },
  {
    "path": "examples/card_transition/components/card/card.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {},\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/card_transition/components/card/card.wxml",
    "content": "<!-- 需要先给定高度，微信 8.0.33 后 share-element 将能够撑开高度 -->\n<view class=\"card\" style=\"height: {{cardWidth / item.imageRatio + 80}}px;\" data-index=\"{{index}}\" data-url=\"{{item.src}}\" data-content=\"{{item.content}}\" data-ratio=\"{{item.imageRatio}}\" data-nickname=\"{{item.nickname}}\" bindtap=\"navigateTo\">\n\t<share-element key=\"se-key{{index}}\" rect-tween-type=\"cubic-bezier(0.4, 0.0, 0.2, 1.0)\" worklet:onframe=\"handleFrame\" transition-on-gesture=\"{{true}}\" style=\"width: 100%; height: 100%;\">\n\t\t<view style=\"position: relative;\">\n\t\t\t<view class=\"card_wrap\">\n\t\t\t\t<image class=\"card_img\" fade-in src=\"{{item.src}}\" style=\"height: {{cardWidth / item.imageRatio}}px; background-color: {{item.color}};\"/>\n\t\t\t\t<view class=\"card_desc\" style=\"color: #000;\">\n\t\t\t\t\t<view class=\"card_content\">{{item.content}}</view>\n\t\t\t\t\t<view class=\"card_footer\">\n\t\t\t\t\t\t<image class=\"card_avatar\" src=\"https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihrwQPXmcn86nOyioRnPAfkrbZteUWsKfEpgoYZ1pk-3TMTc_qXFSElIgkvILR-zzh1Q\" />\n\t\t\t\t\t\t<text class=\"card_nickname\">{{item.nickname}}</text>\n\t\t\t\t\t\t<text class=\"card_like\">{{item.like}}</text>\n\t\t\t\t\t</view>\n\t\t\t\t</view>\n\t\t\t</view>\n\t\t</view>\n\t</share-element>\n</view>"
  },
  {
    "path": "examples/card_transition/components/card/card.wxss",
    "content": ".card {\n  border-radius: 5px;\n  overflow: hidden;\n  flex-shrink: 0;\n  width: 100%;\n  background-color: white;\n}\n\n.card_wrap {\n  position: absolute;\n  transform-origin: 0 0;\n  width: 100%;\n  display: flex;\n  flex-direction: column;\n}\n\n.card_img {\n  width: 100%; \n  border-top-right-radius: 5px;\n  border-top-left-radius: 5px;\n}\n\n.card_content {\n  padding: 8px;\n  font-size: 14px;\n  font-weight: 500;\n  /* height: 54px; */\n  overflow: hidden;\n  box-sizing: border-box;\n}\n\n.card_footer {\n  padding: 0 8px 8px;\n  display: flex;\n  flex-direction: row;\n  font-size: 12px;\n  color: #666666;\n  line-height: 20px;\n}\n\n.card_avatar {\n  width: 20px;\n  height: 20px;\n  border-radius: 50%;\n}\n\n.card_nickname {\n  flex: 1;\n  padding-left: 4px;\n}\n"
  },
  {
    "path": "examples/card_transition/components/navigation-bar/index.js",
    "content": "Component({\n  options: {\n    multipleSlots: true // 在组件定义时的选项中启用多slot支持\n  },\n  /**\n   * 组件的属性列表\n   */\n  properties: {\n    extClass: {\n      type: String,\n      value: ''\n    },\n    title: {\n      type: String,\n      value: ''\n    },\n    background: {\n      type: String,\n      value: ''\n    },\n    color: {\n      type: String,\n      value: ''\n    },\n    back: {\n      type: Boolean,\n      value: true\n    },\n    loading: {\n      type: Boolean,\n      value: false\n    },\n    animated: {\n      // 显示隐藏的时候opacity动画效果\n      type: Boolean,\n      value: true\n    },\n    show: {\n      // 显示隐藏导航，隐藏的时候navigation-bar的高度占位还在\n      type: Boolean,\n      value: true,\n      observer: '_showChange'\n    },\n    // back为true的时候，返回的页面深度\n    delta: {\n      type: Number,\n      value: 1\n    }\n  },\n  /**\n   * 组件的初始数据\n   */\n  data: {\n    displayStyle: ''\n  },\n  attached() {\n    const rect = wx.getMenuButtonBoundingClientRect()\n    wx.getSystemInfo({\n      success: (res) => {\n        this.setData({\n          statusBarHeight: res.statusBarHeight,\n          innerPaddingRight: `padding-right:${res.windowWidth - rect.left}px`,\n          leftWidth: `width:${res.windowWidth - rect.left}px`,\n          navBarHeight: rect.bottom + rect.top - res.statusBarHeight,\n        })\n      }\n    })\n  },\n  /**\n   * 组件的方法列表\n   */\n  methods: {\n    _showChange(show) {\n      const animated = this.data.animated\n      let displayStyle = ''\n      if (animated) {\n        displayStyle = `opacity: ${show ? '1' : '0'};transition: opacity 0.5s;`\n      } else {\n        displayStyle = `display: ${show ? '' : 'none'}`\n      }\n      this.setData({\n        displayStyle\n      })\n    },\n    back() {\n      const data = this.data\n      if (data.delta) {\n        wx.navigateBack({\n          delta: data.delta\n        })\n      }\n      this.triggerEvent('back', { delta: data.delta }, {})\n    }\n  }\n})\n"
  },
  {
    "path": "examples/card_transition/components/navigation-bar/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {},\n  \"addGlobalClass\": true,\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/card_transition/components/navigation-bar/index.wxml",
    "content": "<view class=\"weui-navigation-bar {{extClass}}\">\n  <view class=\"weui-navigation-bar__inner\" style=\"padding-top: {{statusBarHeight}}px; height: {{navBarHeight}}px; color: {{color}}; background: {{background}}; {{displayStyle}}; {{innerPaddingRight}};\">\n    <view class='weui-navigation-bar__left' style=\"{{leftWidth}}\">\n      <block wx:if=\"{{back}}\">\n        <view class=\"weui-navigation-bar__buttons\">\n          <view bindtap=\"back\" class=\"weui-navigation-bar__btn_goback_wrapper\" hover-class=\"weui-active\" aria-role=\"button\" aria-label=\"返回\">\n            <view class=\"weui-navigation-bar__button weui-navigation-bar__btn_goback\"></view>\n          </view>\n        </view>\n      </block>\n      <block wx:else>\n        <slot name=\"left\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__center'>\n      <view wx:if=\"{{loading}}\" class=\"weui-navigation-bar__loading\" aria-role=\"alert\">\n        <view class=\"weui-loading\" style=\"width:{{size.width}}rpx;height:{{size.height}}rpx;\" aria-role=\"img\" aria-label=\"加载中\"></view>\n      </view>\n      <block wx:if=\"{{title}}\">\n        <text>{{title}}</text>\n      </block>\n      <block wx:else>\n        <slot name=\"center\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__right'>\n      <slot name=\"right\"></slot>\n    </view>\n  </view>\n</view>\n"
  },
  {
    "path": "examples/card_transition/components/navigation-bar/index.wxss",
    "content": ".weui-navigation-bar {\n  overflow: hidden;\n  color: rgba(0, 0, 0, .9);\n  width: 100vw;\n}\n\n.weui-navigation-bar__placeholder {\n  background: #f7f7f7;\n  position: relative;\n}\n\n.weui-navigation-bar__inner, .weui-navigation-bar__inner .weui-navigation-bar__left {\n  display: flex;\n  align-items: center;\n  flex-direction: row;\n}\n\n.weui-navigation-bar__inner {\n  position: relative;\n  padding-right: 95px;\n  width: 100vw;\n  box-sizing: border-box;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left {\n  position: relative;\n  width: 95px;\n  padding-left: 16px;\n  box-sizing: border-box;\n}\n\n.weui-navigation-bar__btn_goback_wrapper {\n  padding: 11px 18px 11px 16px;\n  margin: -11px -18px -11px -16px;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left .weui-navigation-bar__btn_goback {\n  font-size: 12px;\n  width: 12px;\n  height: 24px;\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E\") no-repeat 50% 50%;\n  background-size: cover;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__center {\n  font-size: 17px;\n  text-align: center;\n  position: relative;\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-weight: bold;\n}\n\n@media(prefers-color-scheme: dark) {\n  .weui-navigation-bar {\n    color: hsla(0, 0%, 100%, .8);\n  }\n  .weui-navigation-bar__inner {\n    background-color: #1f1f1f;\n  }\n}\n"
  },
  {
    "path": "examples/card_transition/pages/detail/detail.js",
    "content": "import { Curves, CurveAnimation, lerp } from '../list/route'\nimport { clamp } from '../list/utils'\n\nconst { screenWidth } = wx.getSystemInfoSync()\nconst { shared, timing, Easing } = wx.worklet\n\nconst GestureState = {\n  POSSIBLE: 0, // 0 此时手势未识别，如 panDown等\n  BEGIN: 1, // 1 手势已识别\n  ACTIVE: 2, // 2 连续手势活跃状态\n  END: 3, // 3 手势终止\n  CANCELLED: 4, // 4 手势取消，\n}\n\nconst transLowerBound = -1/3 * screenWidth\nconst transUpperBound = 2/3 * screenWidth\n\nComponent({\n  properties: {\n    index: {\n      type: Number,\n      value: -1,\n    },\n    url: {\n      type: String,\n      value: '',\n    },\n    content: {\n      type: String,\n      value: '',\n    },\n    ratio: {\n      type: Number,\n      value: 1\n    },\n    nickname: {\n      type: String,\n      value: '',\n    }\n  },\n  data: {\n    swiperHeight: 0,\n    imageList: [\n      'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr7lTnuuiwGJPwwjxDVYbDolj05sAxd5cOESVZt4_nl1KwzkiDWTvG56LuhE45xAaZA',\n      'https://res.wx.qq.com/op_res/Ak_VAL-nLvq6laAMVJA86rf3NAZ2vY86v757dfja16Z95xtoxk4BWWDuTCPT-pD1SjGGIddUsH0l6C8Yu5LJlw'\n    ]\n  },\n  lifetimes: {\n    created() {\n      this.startX = shared(0)\n      this.startY = shared(0)\n      this.transX = shared(0)\n      this.transY = shared(0)\n      this.isInteracting = shared(false)\n    },\n    attached() {\n      this.setData({\n        swiperHeight: screenWidth / this.data.ratio\n      })\n      this.customRouteContext = wx.router?.getRouteContext(this);\n      const { \n        primaryAnimation,\n        primaryAnimationStatus,\n        userGestureInProgress,\n        shareEleTop\n      } = this.customRouteContext || {}\n\n      // 根据进入或返回使用不同曲线换算到的值\n      const _curvePrimaryAnimation = CurveAnimation({\n        animation: primaryAnimation,\n        animationStatus: primaryAnimationStatus,\n        curve: Easing.in(Curves.fastOutSlowIn),\n        reverseCurve: Easing.out(Curves.fastOutSlowIn)\n      })\n\n      this.applyAnimatedStyle('.detail-content', () => {\n        'worklet'\n        return {\n          opacity: _curvePrimaryAnimation.value\n        }\n      })\n\n      this.applyAnimatedStyle('#fake-host', () => {\n        'worklet'\n        // pan 手势释放后，触发返回动画，userGestureInProgress 由 startUserGesture() 标记\n        if (userGestureInProgress.value && \n          globalThis['RouteCardSrcRect'] && \n          globalThis['RouteCardSrcRect'].value != undefined\n        ) {\n          const begin = globalThis['RouteCardSrcRect'].value\n          const end = globalThis['RouteCardDestRect'].value\n          \n          const t = 1 - _curvePrimaryAnimation.value\n          const shareEleX = lerp(begin.left, end.left, t)\n          const shareEleY = lerp(begin.top, end.top, t)\n          const shareEleW = lerp(begin.width, end.width, t)\n          \n          const scale = shareEleW / screenWidth\n          const transX = shareEleX\n          // shareEleTop 是完全展开时 share-element 的 top 值，换比例换算\n          // 使得缩放过程中，最后图片顶部对齐卡片图片顶部\n          const transY = shareEleY - shareEleTop.value * scale\n\n          return {\n            transform: `translateX(${transX}px) translateY(${transY}px) scale(${scale})`,\n            transformOrigin: '0 0',\n          }\n        }\n        // pan 手势移动阶段\n        const transX = this.transX.value\n        const transY = this.transY.value\n        // 根据横坐标位移比例缩放\n        const scale = clamp(1 - transX / screenWidth * 0.5, 0, 1)\n        return {\n          transform: `translateX(${transX}px) translateY(${transY}px) scale(${scale})`,\n          transformOrigin: '50% 50%'\n        }\n      }, { immediate: false })\n    },\n  },\n  methods: {\n    handlePanGesture(e) {\n      'worklet'\n      const {\n        startUserGesture,\n        stopUserGesture,\n        primaryAnimation,\n        didPop,\n      } = this.customRouteContext\n\n      if (e.state === GestureState.BEGIN) {\n        this.startX.value = e.absoluteX\n        this.startY.value = e.absoluteY\n      } else if (e.state === GestureState.ACTIVE) {\n        // 往右滑时\n        if (e.deltaX > 0 && !this.isInteracting.value) {\n          this.isInteracting.value = true\n        }\n        if (!this.isInteracting.value) return\n\n        const transX = e.absoluteX - this.startX.value\n        this.transX.value = clamp(transX, transLowerBound, transUpperBound)\n        this.transY.value = e.absoluteY - this.startY.value\n      } else if (e.state === GestureState.END || e.state === GestureState.CANCELLED) {\n        if (!this.isInteracting.value) return\n        this.isInteracting.value = false\n\n        // 是要返回还是取消返回\n        let shouldFinish = false\n        if (e.velocityX > 500 || this.transX.value / screenWidth > 0.25) {\n          shouldFinish = true\n        }\n        if (shouldFinish) {\n          startUserGesture()\n          primaryAnimation.value = timing(0.0, {\n            duration: 180,\n            easing: Easing.linear\n          }, () => {\n            'worklet'\n            stopUserGesture()\n            didPop()\n          })\n        } else {\n          this.transX.value = timing(0.0, { duration: 100 })\n          this.transY.value = timing(0.0, { duration: 100 })\n        }\n      }\n    },\n  },\n})\n"
  },
  {
    "path": "examples/card_transition/pages/detail/detail.json",
    "content": "{\n  \"usingComponents\": {},\n  \"backgroundColorContent\": \"#00000000\",\n  \"disableScroll\": true,\n  \"navigationStyle\": \"custom\"\n}"
  },
  {
    "path": "examples/card_transition/pages/detail/detail.wxml",
    "content": "<pan-gesture-handler worklet:ongesture=\"handlePanGesture\">\n\t<view id=\"fake-host\">\n\t\t<view id=\"page\">\n\t\t\t<view class=\"navigation-bar\">\n\t\t\t\t<view class=\"navigation-bar-content\">\n\t\t\t\t\t<image class=\"navigation-bar-avatar\" src=\"https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihrwQPXmcn86nOyioRnPAfkrbZteUWsKfEpgoYZ1pk-3TMTc_qXFSElIgkvILR-zzh1Q\" />\n\t\t\t\t\t<view class=\"navigation-bar-title\">{{nickname}}</view>\n\t\t\t\t\t<view class=\"navigation-bar-follow\">关注</view>\n\t\t\t\t</view>\n\t\t\t</view>\n\t\t\t<share-element\n\t\t\t key=\"se-key{{index}}\"\n\t\t\t shuttle-on-push=\"from\"\n\t\t\t transition-on-gesture=\"{{true}}\"\n\t\t\t rect-tween-type=\"cubic-bezier(0.4, 0.0, 0.2, 1.0)\"\n\t\t\t style=\"width: 100%; height: 0px;\"\n\t\t\t>\n\t\t\t\t<view/>\n\t\t\t</share-element>\n\n\t\t\t<scroll-view\n\t\t\t scroll-y\n\t\t\t style=\"flex: 1; width: 100%; overflow: hidden;\"\n\t\t\t type=\"list\"\n\t\t\t show-scrollbar=\"{{false}}\"\n\t\t\t>\n\t\t\t\t<view>\n          <!-- swiper 还可以做个手势协商，此处省略，即在当前是第一张图时，右滑触动页面返回交互 -->\n\t\t\t\t\t<swiper id=\"swiper\" style=\"width: 100%; height: {{swiperHeight}}px;\" indicator-dots>\n\t\t\t\t\t\t<swiper-item>\n\t\t\t\t\t\t\t<image class=\"detail-image\" mode=\"widthFix\" src=\"{{url}}\" />\n\t\t\t\t\t\t</swiper-item>\n\t\t\t\t\t\t<swiper-item wx:for=\"{{imageList}}\" wx:key=\"index\">\n\t\t\t\t\t\t\t<image class=\"detail-image\" mode=\"widthFix\" src=\"{{item}}\" />\n\t\t\t\t\t\t</swiper-item>\n\t\t\t\t\t</swiper>\n\t\t\t\t</view>\n\n\t\t\t\t<view class=\"detail-content\">\n\t\t\t\t\t<view class=\"detail-title\">{{content}}</view>\n          <view class=\"detail-p\" style=\"padding-bottom: 20px;\" />\n\t\t\t\t\t<view class=\"detail-p\">🔥 Skyline 以性能为首要目标，提供更为接近原生的用户体验，Skyline 具有以下特点：</view>\n          <view class=\"detail-p\" style=\"padding-bottom: 10px;\" />\n\t\t\t\t\t<view class=\"detail-p\">🌟 界面更不容易被逻辑阻塞，进一步减少卡顿</view>\n\t\t\t\t\t<view class=\"detail-p\">🌟 无需为每个页面新建一个 JS 引擎实例（WebView），减少了内存、时间开销</view>\n\t\t\t\t\t<view class=\"detail-p\">🌟 框架可以在页面之间共享更多的资源，进一步减少运行时内存、时间开销</view>\n\t\t\t\t\t<view class=\"detail-p\">🌟 框架的代码之间无需再通过 JSBridge 进行数据交换，减少了大量通信时间开销</view>\n\t\t\t\t\t<view class=\"detail-p\" style=\"padding-bottom: 20px;\" />\n\t\t\t\t\t<view class=\"detail-p\">👇👇👇 支持了一些 Web 所缺失的但很重要的能力，以满足开发者实现更好的交互体验：</view>\n          <view class=\"detail-p\" style=\"padding-bottom: 10px;\" />\n\t\t\t\t\t<view class=\"detail-p\">🔸 Worklet 动画：能够在渲染线程同步运行动画相关逻辑。</view>\n\t\t\t\t\t<view class=\"detail-p\">🔸 手势系统：在渲染线程同步监听手势、执行手势相关逻辑；支持手势协商处理；</view>\n\t\t\t\t\t<view class=\"detail-p\">🔸 自定义路由：实现自定义路由动画和交互。</view>\n\t\t\t\t\t<view class=\"detail-p\">🔸 共享元素动画：将上一个页面的元素“共享”到下一个页面，并伴随着过渡动画。</view>\n\t\t\t\t</view>\n\t\t\t</scroll-view>\n\t\t\t<view class=\"footer\">\n\t\t\t\t<view class=\"footer-content\">\n\t\t\t\t\t<input class=\"footer-input\" placeholder=\"说点什么...\" />\n\t\t\t\t\t<span>\n\t\t\t\t\t\t<text class=\"footer-icon\">❤️</text>\n\t\t\t\t\t\t317\n\t\t\t\t\t</span>\n\t\t\t\t\t<span>\n\t\t\t\t\t\t<text class=\"footer-icon\">⭐️</text>\n\t\t\t\t\t\t723\n\t\t\t\t\t</span>\n\t\t\t\t</view>\n\t\t\t</view>\n\t\t</view>\n\t</view>\n</pan-gesture-handler>\n\n"
  },
  {
    "path": "examples/card_transition/pages/detail/detail.wxss",
    "content": "page {\n  display: flex;\n  flex-direction: column;\n  height: 100vh;\n  background-color: transparent;\n}\n\nview, scroll-view, page, image {\n  box-sizing: border-box;\n}\n\n#fake-host {\n  background-color: #FFF;\n  border-radius: 10px;\n  overflow: hidden;\n}\n\n#page {\n  display: flex;\n  flex-direction: column;\n  height: 100vh;\n}\n\n.navigation-bar {\n  padding-top: 44px;\n  padding-top: env(safe-area-inset-top);\n  background-color: white;\n}\n\n.navigation-bar-content {\n  height: 44px;\n  display: flex;\n  flex-direction: row;\n  padding: 0 20px;\n  justify-content: center;\n  align-items: center;\n  font-size: 14px;\n}\n\n.navigation-bar-avatar {\n  width: 24px;\n  height: 24px;\n  border-radius: 100%;\n}\n\n.navigation-bar-title {\n  flex: 1;\n  padding-left: 8px;\n}\n\n.navigation-bar-follow {\n  color: red;\n  border: .5px solid red;\n  border-radius: 15px;\n  padding: 3px 15px;\n  margin-right: 80px;\n}\n\n.detail-image {\n  width: 100%;\n  height: 100%;\n}\n\n.detail-content {\n  padding: 15px;\n}\n\n.detail-title {\n  font-weight: bold;\n  font-size: 18px;\n  padding-bottom: 3px;\n}\n\n.detail-p {\n  padding-bottom: 2px;\n}\n\n.footer {\n  padding-bottom: env(safe-area-inset-bottom);\n  font-size: 14px;\n}\n\n.footer-content {\n  display: flex;\n  align-items: center;\n  flex-direction: row;\n  height: 50px;\n}\n\n.footer .footer-input {\n  flex: 1;\n  background-color: #EEE;\n  margin-left: 15px;\n  margin-right: 15px;\n  padding-left: 15px;\n  height: 40px;\n  border-radius: 20px;\n}\n\n.footer span {\n  padding-right: 15px;\n}\n\n.footer-icon {\n font-size: 26px;\n}\n"
  },
  {
    "path": "examples/card_transition/pages/list/list.js",
    "content": "import { installRouteBuilder } from './route'\nimport { generateGridList, compareVersion } from './utils'\n\nconst { screenWidth } = wx.getSystemInfoSync()\n\nComponent({\n  data: {\n    padding: 4,\n    gridList: generateGridList(100, 2),\n    cardWidth: (screenWidth - 4 * 2 - 4) / 2, // 减去间距\n  },\n\n  lifetimes: {\n    created() {\n      const {SDKVersion} = wx.getSystemInfoSync()\n      if (compareVersion(SDKVersion, '2.30.1') < 0) {\n        wx.showModal({\n          content: '基础库版本低于 v2.30.1 可能会有显示问题，建议升级微信体验。',\n          showCancel: false\n        })\n      }\n      installRouteBuilder()\n    },\n  },\n})\n"
  },
  {
    "path": "examples/card_transition/pages/list/list.json",
    "content": "{\n  \"usingComponents\": {\n    \"navigation-bar\": \"../../components/navigation-bar/index\",\n    \"card\": \"../../components/card/card\"\n  },\n  \"disableScroll\": true,\n  \"navigationStyle\": \"custom\"\n}"
  },
  {
    "path": "examples/card_transition/pages/list/list.wxml",
    "content": "<navigation-bar back title=\"发现\"></navigation-bar>\n\n<scroll-view\n scroll-y\n style=\"flex: 1; width: 100%; overflow: hidden; padding: {{padding}}px; padding-bottom: 0;\"\n type=\"custom\"\n show-scrollbar=\"{{false}}\"\n>\n  <grid-view\n    type=\"masonry\"\n    cross-axis-count=\"2\"\n    cross-axis-gap=\"4\"\n    main-axis-gap=\"4\"\n  >\n    <card\n      wx:for=\"{{gridList}}\"\n      wx:key=\"id\"\n      card-width=\"{{cardWidth}}\"\n      index=\"{{index}}\"\n      item=\"{{item}}\"\n    />\n  </grid-view>\n</scroll-view>\n\n"
  },
  {
    "path": "examples/card_transition/pages/list/list.wxss",
    "content": "page {\n  display: flex;\n  flex-direction: column;\n  height: 100vh;\n}\n\nscroll-view {\n  background-color: #f4f4f4;\n}\n\nview, scroll-view, page, image {\n  box-sizing: border-box;\n}\n"
  },
  {
    "path": "examples/card_transition/pages/list/route.js",
    "content": "const AnimationStatus = {\n  dismissed: 0, // The animation is stopped at the beginning.\n  forward: 1, // The animation is running from beginning to end.\n  reverse: 2, // The animation is running backwards, from end to beginning.\n  completed: 3, // The animation is stopped at the end.\n}\n\nconst { Easing, shared } = wx.worklet\n\nexport const Curves = {\n  linearToEaseOut: Easing.cubicBezier(0.35, 0.91, 0.33, 0.97),\n  easeInToLinear: Easing.cubicBezier(0.67, 0.03, 0.65, 0.09),\n  fastOutSlowIn: Easing.cubicBezier(0.4, 0.0, 0.2, 1.0),\n  slowOutFastIn: Easing.cubicBezier(0.0, 0.8, 1.0, 0.6),\n  easeOutCubic: Easing.cubicBezier(0.215, 0.61, 0.355, 1.0),\n}\n\nexport function CurveAnimation({\n  animation, animationStatus, curve, reverseCurve\n}) {\n  const { derived } = wx.worklet\n\n  return derived(() => {\n    'worklet'\n\n    const useForwardCurve = !reverseCurve || animationStatus.value !== AnimationStatus.reverse\n    const activeCurve = useForwardCurve ? curve : reverseCurve\n\n    const t = animation.value\n    if (!activeCurve) return t\n    if (t === 0 || t === 1) return t\n    return activeCurve(t)\n  })\n}\n\nexport const lerp = (begin, end, t) => {\n  'worklet'\n  return begin + (end - begin) * t\n}\n\nconst ScaleTransitionRouteBuilder = (routeContext) => {\n  const {\n    primaryAnimation,\n    primaryAnimationStatus,\n    userGestureInProgress,\n  } = routeContext\n\n  const shareEleTop = shared(0)\n  routeContext.shareEleTop = shareEleTop\n\n  const _curvePrimaryAnimation = CurveAnimation({\n    animation: primaryAnimation,\n    animationStatus: primaryAnimationStatus,\n    curve: Easing.in(Curves.fastOutSlowIn),\n    reverseCurve: Easing.out(Curves.fastOutSlowIn)\n  })\n\n  // 每次路由动画结束（进入或返回）都会重置一下\n  const reset = () => {\n    'worklet'\n    if (globalThis['RouteCardSrcRect']) {\n      globalThis['RouteCardSrcRect'].value = undefined\n    }\n    if (globalThis['RouteCardDestRect']) {\n      globalThis['RouteCardDestRect'].value = undefined\n    }\n  }\n\n  const handlePrimaryAnimation = () => {\n    'worklet'\n    const status = primaryAnimationStatus.value\n    // 手势返回时，动画在详情页处理，此处顶层节点只做整体透明度淡出\n    if (userGestureInProgress.value) {\n      return {\n        opacity: Easing.out(Easing.cubicBezier(0.5, 0, 0.7, 0.5)(primaryAnimation.value)),\n      }\n    }\n\n    if (status == AnimationStatus.dismissed) {\n      reset()\n      return {\n        transform: `translate(0, 0) scale(0)`,\n      }\n    }\n\n    if (status == AnimationStatus.completed ) {\n      reset()\n      return {\n        transform: `translate(0, 0) scale(1)`,\n      }\n    }\n\n    let transX = 0\n    let transY = 0\n    let scale = status === AnimationStatus.reverse ? 1 : 0\n\n    // 进入或者接口返回\n    if (globalThis['RouteCardSrcRect'] && globalThis['RouteCardSrcRect'].value != undefined) {\n      const begin = globalThis['RouteCardSrcRect'].value\n      const end = globalThis['RouteCardDestRect'].value\n\n      if (status === AnimationStatus.forward) {\n        shareEleTop.value = end.top\n      }\n\n      let t = _curvePrimaryAnimation.value\n      if (status === AnimationStatus.reverse || status === AnimationStatus.dismissed) {\n        t = 1 - t\n      }\n\n      const shareEleX = lerp(begin.left, end.left, t)\n      const shareEleY = lerp(begin.top, end.top, t)\n      const shareEleW = lerp(begin.width, end.width, t)\n\n      transX = shareEleX\n      if (status === AnimationStatus.reverse) {\n        scale = shareEleW / begin.width\n        transY = shareEleY - begin.top * scale\n      } else {\n        scale = shareEleW / end.width\n        transY = shareEleY - end.top * scale\n      }\n    }\n\n    return {\n      transform: `translate(${transX}px, ${transY}px) scale(${scale})`,\n      transformOrigin: '0 0',\n      opacity: _curvePrimaryAnimation.value,\n    }\n  }\n\n  return {\n    opaque: false,\n    handlePrimaryAnimation,\n    transitionDuration: 250,\n    reverseTransitionDuration: 250,\n    canTransitionTo: false,\n    canTransitionFrom: false,\n    barrierColor: \"rgba(0, 0, 0, 0.3)\",\n  }\n}\n\nlet hasInstalled = false\nexport function installRouteBuilder() {\n  if (hasInstalled) {\n    return\n  }\n  wx.router.addRouteBuilder('CardScaleTransition', ScaleTransitionRouteBuilder)\n  hasInstalled = true\n}\n"
  },
  {
    "path": "examples/card_transition/pages/list/utils.js",
    "content": "export const lightBlue = {\n  0: '#E1F5FE',\n  100: '#B3E5FC',\n  200: '#81D4FA',\n  300: '#4FC3F7',\n  400: '#29B6F6',\n  500: '#03A9F4',\n  600: '#039BE5',\n  700: '#0288D1',\n  800: '#0277BD',\n  900: '#01579B',\n}\n\nexport const generateList = (childCount) => {\n  const ans = []\n  for (let i = 0; i < childCount; i++) {\n    ans.push({\n      id: i,\n      color: lightBlue[`${100 * (i % 9)}`],\n    })\n  }\n  return ans\n}\n\nconst contents = [\n  '小程序推出 Skyline 新渲染框架啦',\n  '推荐 Skyline，使用后体验流畅很多~',\n  '开发必备！共享元素、自定义路由、手势系统',\n  'Hayya Hayya！我用小程序啦',\n]\n\nconst nicknames = [\n  'REX',\n  'BINNIE',\n  'ERIC',\n  'SANFORD',\n]\n\nconst imageRatio = [\n  {\n    width: 3,\n    height: 4,\n    imageRatio: 3 / 4,\n  },\n  {\n    width: 4,\n    height: 3,\n    imageRatio: 4 / 3,\n  },\n  {\n    width: 1,\n    height: 1,\n    imageRatio: 1 / 1,\n  },\n]\n\nconst imageList = [\n  // 3:4\n  [\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr_TJOaxvM0jTWnZCPVx5tYhqZIIAWcwZ-wjkthDNgUPon6gB8cS1-4Gmj9Fa0emByQ',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr5TiaeMo-e_G_0VkoAgrUpJDa0vkq7A-ZqnGdXPqENXxwOpNm6WNaukJzkaNpe2l4g',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr3Vg3QwFEkRrtGVFfuis3HPsfPRAimoR3xrmxA6WqSP6gqLYxpQR70H0Mjd82xRvLg',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr57xPb6otBpyKgqlzjXvSaLKB_SPr5oYFTYCYUbk6bCwyLvvPWUVpsNuYRjVNouuDw',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr8oVdhjDzwpGQWkUNT3VLWmNYEetJXErnWq48jD0zVELo45qmUAdu7jCgFskY6Eh8w',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr1x4v1gTqT3MrC7LtVTjQXb_9hd9vbCf12guLPXiMXd0G7IUnLQXkOa-o1eNyAJ_nA',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr7lTnuuiwGJPwwjxDVYbDolj05sAxd5cOESVZt4_nl1KwzkiDWTvG56LuhE45xAaZA'\n  ],\n  // 4:3\n  [\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr87sFqvqtkPc7qeZdary_8crGWuX_SOb72lupHA7sWx0dti3JrJXdP_lwm0ZtvINXg',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr3vA4i7lSkWNR0BRe_g4A-_lo5MYYlkks8oHLoZzXjqAm_M3RvDAXtn9UUgZuQtVBA',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr5Ifsj1_cRjONPrw-gUgq8g6BNH8sYQ3kBBQas5JAeMN0zsCBY9gmz3D7kj_GOWfHw',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr1IwceePWSJ_EhG4QedvnFKN6v_mNlNuwG2FkAIoOhx_1fyCDEqtHWSktSrPmLvTpw',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihrzz951X66QJWV_Oj4MT6XImEk-wFlNZP6mJE1Vt-ybtD1UK7ARlhOBl9bizrC5KA9g',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihrxFO1zooQxE0ufna7fMaqrU-Pp4Dm2rw5dFcTdBymLTijegIFw3WcVD1rUyLD4XTig',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr6WcfJCajSnCm4CNu5oQ5HPsPqyzWD-vtFVuJDZOhMpcG1iN0tvOsvS8DUgn3qO8UA',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr4HKYTq7-4l-F47z8u2QbvNsjcTEA3Cu5-4wQpBGPeWKCh66Ho5W42fn3naWuN2NJg',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr8PdfZEyicDsJiFPBw8MAjve2UKbzLds_-IZW_Q0EYUbboQk-31FeTkFmzuNzCfLHg'\n  ],\n  // 1:1\n  [\n    'https://res.wx.qq.com/op_res/KSWft_GRyQ3WEzVUTCSWs7HaJh0lgdPce6Uon3dhNpZ3R3sTVA3NLrOORpMDGaBl5P8QkzHZCaOErPlma2sAow',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihrwcWdDUeblb42H9kVfv14Eru-W62xBL1bUXbfwZbaJG7_JrKvnAKvdVCQJkS3PX3IQ',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr13IooGqagGNd7x5NTGbtrz4g0NrIVLLJ2KSx-BcYpaGMTpnv-pUB_iexsCzQC4wZg',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihrxD9Yj0ZHr0C5YMm7qYRo2fqji9kH4CS6LUyQf4YXzHzK3BW0FFNZiTQb6AK9bp1WA',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr5yBy6GoASjPro9uFIUZVFdiDIjiJObbopuhr7PUXnsTLQ537ujpIBxyX2Ln2gRu0w',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihrzhx4m_v7j5nYGhkUG5h-dulp3X7FxpQVY8L1QzVqPROJHUcK0mO38isUiclpbae_Q',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr_VrnIzjbAVDL2cmG0wjYsNZv1l_lacmGCshp9OEz3QcPnn9YymbITplyQS5T5C-VA',\n    'https://res.wx.qq.com/op_res/BqgN85sXxTbk1kynEEihr0m2rsO-Y1l6Wsz_sFyu7vJj_ZTfI7GABbstLg4GUDTZVeZCKgDADCmsDjmF8rG7dw'\n  ]\n]\n\nexport const generateGridList = (childCount, columns) => {\n  const ans = []\n  for (let i = 0; i < childCount; i++) {\n    const ratioIdx = Math.floor(Math.random() * imageRatio.length)\n    const ratio = imageRatio[ratioIdx]\n    const img = imageList[ratioIdx][Math.floor(Math.random() * imageList[ratioIdx].length)]\n    ans.push({\n      id: i,\n      src: img,\n      ...ratio,\n      like: Math.floor(Math.random() * 10000),\n      content: contents[Math.floor(Math.random() * contents.length)],\n      nickname: nicknames[Math.floor(Math.random() * nicknames.length)],\n    })\n  }\n  return ans\n}\n\n\nexport const clamp = function (cur, lowerBound, upperBound) {\n  'worklet';\n  if (cur > upperBound) return upperBound;\n  if (cur < lowerBound) return lowerBound;\n  return cur;\n};\n\nexport const compareVersion = function (v1, v2) {\n  v1 = v1.split('.')\n  v2 = v2.split('.')\n  const len = Math.max(v1.length, v2.length)\n\n  while (v1.length < len) {\n    v1.push('0')\n  }\n  while (v2.length < len) {\n    v2.push('0')\n  }\n\n  for (let i = 0; i < len; i++) {\n    const num1 = parseInt(v1[i], 10)\n    const num2 = parseInt(v2[i], 10)\n\n    if (num1 > num2) {\n      return 1\n    } else if (num1 < num2) {\n      return -1\n    }\n  }\n\n  return 0\n}"
  },
  {
    "path": "examples/card_transition/project.config.json",
    "content": "{\n  \"appid\": \"wxe5f52902cf4de896\",\n  \"compileType\": \"miniprogram\",\n  \"libVersion\": \"latest\",\n  \"packOptions\": {\n    \"ignore\": [],\n    \"include\": []\n  },\n  \"setting\": {\n    \"coverView\": true,\n    \"es6\": true,\n    \"postcss\": true,\n    \"minified\": true,\n    \"enhance\": true,\n    \"showShadowRootInWxmlPanel\": true,\n    \"packNpmRelationList\": [],\n    \"babelSetting\": {\n      \"ignore\": [],\n      \"disablePlugins\": [],\n      \"outputPath\": \"\"\n    },\n    \"condition\": false,\n    \"skylineRenderEnable\": false,\n    \"compileWorklet\": true\n  },\n  \"condition\": {},\n  \"editorSetting\": {\n    \"tabIndent\": \"insertSpaces\",\n    \"tabSize\": 2\n  }\n}"
  },
  {
    "path": "examples/card_transition/project.private.config.json",
    "content": "{\n  \"description\": \"项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档：https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html\",\n  \"projectname\": \"card_transition\",\n  \"setting\": {\n    \"compileHotReLoad\": false,\n    \"skylineRenderEnable\": false\n  },\n  \"condition\": {\n    \"miniprogram\": {\n      \"list\": [\n        {\n          \"name\": \"\",\n          \"pathName\": \"detail/detail\",\n          \"query\": \"index=1&url=https%3A%2F%2Fpicsum.photos%2F300%2F400%3Frandom%3D1&content=Hayya Hayya！我来小红书啦&ratio=0.75\",\n          \"launchMode\": \"default\",\n          \"scene\": null\n        },\n        {\n          \"name\": \"\",\n          \"pathName\": \"list/list\",\n          \"query\": \"\",\n          \"launchMode\": \"default\",\n          \"scene\": null\n        }\n      ]\n    }\n  },\n  \"libVersion\": \"latest\"\n}"
  },
  {
    "path": "examples/card_transition/sitemap.json",
    "content": "{\n  \"desc\": \"关于本文件的更多信息，请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html\",\n  \"rules\": [{\n  \"action\": \"allow\",\n  \"page\": \"*\"\n  }]\n}"
  },
  {
    "path": "examples/expanded-scroll-view/.eslintrc.js",
    "content": "/*\n * Eslint config file\n * Documentation: https://eslint.org/docs/user-guide/configuring/\n * Install the Eslint extension before using this feature.\n */\nmodule.exports = {\n  env: {\n    es6: true,\n    browser: true,\n    node: true,\n  },\n  ecmaFeatures: {\n    modules: true,\n  },\n  parserOptions: {\n    ecmaVersion: 2018,\n    sourceType: 'module',\n  },\n  globals: {\n    wx: true,\n    App: true,\n    Page: true,\n    getCurrentPages: true,\n    getApp: true,\n    Component: true,\n    requirePlugin: true,\n    requireMiniProgram: true,\n  },\n  // extends: 'eslint:recommended',\n  rules: {},\n}\n"
  },
  {
    "path": "examples/expanded-scroll-view/app.js",
    "content": "// app.js\nApp({})\n"
  },
  {
    "path": "examples/expanded-scroll-view/app.json",
    "content": "{\n  \"pages\": [\n    \"pages/index/index\"\n  ],\n  \"window\": {\n    \"backgroundTextStyle\": \"light\",\n    \"navigationBarBackgroundColor\": \"#fff\",\n    \"navigationBarTitleText\": \"Weixin\",\n    \"navigationBarTextStyle\": \"black\"\n  },\n  \"style\": \"v2\",\n  \"renderer\": \"skyline\",\n  \"rendererOptions\": {\n    \"skyline\": {\n      \"defaultDisplayBlock\": true,\n      \"disableABTest\": true\n    }\n  },\n  \"lazyCodeLoading\": \"requiredComponents\",\n  \"sitemapLocation\": \"sitemap.json\"\n}"
  },
  {
    "path": "examples/expanded-scroll-view/app.wxss",
    "content": ""
  },
  {
    "path": "examples/expanded-scroll-view/pages/index/index.js",
    "content": "import { getCategory, getProducts } from '../../util'\n\nconst { shared, timing, decay, derived, runOnUI, runOnJS } = wx.worklet\n\nconst categories = getCategory()\nconst products = getProducts()\n\nconst GestureState = {\n  POSSIBLE: 0, // 0 此时手势未识别，如 panDown等\n  BEGIN: 1, // 1 手势已识别\n  ACTIVE: 2, // 2 连续手势活跃状态\n  END: 3, // 3 手势终止\n  CANCELLED: 4, // 4 手势取消，\n}\n\nconst InteractionState = {\n  INITIAL: 0,\n  ANIMATING: 1,\n  UNFOLD: 2,\n  SCROLL: 3,\n  RESET: 4,\n}\n\nconst clamp = (num, min, max) => {\n  'worklet'\n  return Math.min(Math.max(num, min), max)\n}\n\nComponent({\n  data: {\n    statusBarHeight: 0,\n    categories,\n    selected: 0,\n    list: categories.map(category => {\n      return {\n        header: category,\n        data: products\n      }\n    }),\n    expand: false,\n  },\n  lifetimes: {\n    created() {\n      this._interactionState = shared(0)\n      this._tabsTop = shared(0)\n      this._mainHeight = shared(700)\n      this._startY = shared(0)\n      this._translY = shared(0)\n      this._deltaY = shared(0)\n      this._scrollTop = shared(0)\n      this._canPan = shared(true)\n    },\n    attached() {\n      const windowInfo = wx.getWindowInfo()\n      this.setData({\n        statusBarHeight: windowInfo.statusBarHeight\n      })\n\n      const updateNavBackColor = this.updateNavBackColor.bind(this)\n      this._listenTranslY = derived(() => {\n        'worklet'\n        const expand = this._translY.value < -this._tabsTop.value / 2\n        runOnJS(updateNavBackColor)(expand)\n      })\n    },\n  },\n  methods: {\n    onBannerLoaded() {\n      this.createSelectorQuery().select('.tabs').boundingClientRect(tabs => {\n        this.createSelectorQuery().select('.navigation-bar').boundingClientRect(nav => {\n          this._tabsTop.value = tabs.top - nav.height\n          this.bindAnimatedStyle()\n        }).exec()\n      }).exec()\n      this.createSelectorQuery().select('.main').boundingClientRect(main => {\n        this._mainHeight.value = main.height\n      }).exec()\n    },\n    updateNavBackColor(expand) {\n      this.setData({expand})\n    },\n    bindAnimatedStyle() {\n      /* 下面是 从顶部往上拉的动画部分 */\n      this.applyAnimatedStyle('.page', () => {\n        'worklet'\n        const translY = clamp(this._translY.value, -this._tabsTop.value, 0)\n        return {\n          transform: `translateY(${translY}px)`\n        }\n      })\n      this.applyAnimatedStyle('.navigation-bar', () => {\n        'worklet'\n        const translY = clamp(this._translY.value, -this._tabsTop.value, 0)\n        const opacity = translY / -this._tabsTop.value\n        return {\n          backgroundColor: `rgba(255, 255, 255, ${opacity})`\n        }\n      })\n      // color 是继承属性，暂不支持\n      // this.applyAnimatedStyle('.navigation-bar-content .back', () => {\n      //   'worklet'\n      //   const translY = clamp(this._translY.value, -this._tabsTop.value, 0)\n      //   const value = (1 - translY / -this._tabsTop.value) * 255 | 0\n      //   return {\n      //     color: `rgb(${value}, ${value}, ${value})`\n      //   }\n      // })\n      this.applyAnimatedStyle('.search-input', () => {\n        'worklet'\n        const translY = clamp(this._translY.value, -this._tabsTop.value, 0)\n        const percentage = translY / -this._tabsTop.value\n        return {\n          width: `${percentage * 60 + 40}%`,\n          opacity: percentage,\n        }\n      })\n\n      /* 下面是 到顶往下拉的动画部分 */\n      this.applyAnimatedStyle('.main', () => {\n        'worklet'\n        const translY = clamp(this._translY.value, 0, Number.MAX_VALUE)\n        return {\n          transform: `translateY(${translY}px)`\n        }\n      })\n      this.applyAnimatedStyle('.header-shop-info-simple', () => {\n        'worklet'\n        const min = 50\n        const max = 100\n        const translY = clamp(this._translY.value, min, max) - min\n        return {\n          opacity: 1 - (translY / (max - min))\n        }\n      })\n      this.applyAnimatedStyle('.header-shop-info-detail', () => {\n        'worklet'\n        const min = 100\n        const max = 150\n        const translY = clamp(this._translY.value, min, max) - min\n        return {\n          opacity: translY / (max - min)\n        }\n      })\n    },\n    handlePan(e) {\n      'worklet'\n      const _interactionState = this._interactionState\n      if (this._interactionState.value === InteractionState.ANIMATING) {\n        return\n      }\n      if (this._interactionState.value === InteractionState.RESET) {\n        // 在 gesture active 期间触发的动画，在手指起来时才能重置回 INITIAL\n        if (e.state === GestureState.END || e.state === GestureState.CANCELLED) {\n          this._interactionState.value = InteractionState.INITIAL\n        }\n        return\n      }\n\n      if (e.state === GestureState.BEGIN) {\n        const lastTranslY = clamp(this._translY.value, -this._tabsTop.value, 0)\n        this._startY.value = e.absoluteY - lastTranslY\n      }\n\n      if (e.state === GestureState.ACTIVE) {\n        if (this._interactionState.value === InteractionState.UNFOLD) {\n          // 展开状态下，往上滑才折叠起来\n          if (e.absoluteY - this._startY.value < 0) {\n            this._interactionState.value = InteractionState.ANIMATING\n            this._translY.value = timing(0.0, { duration: 250 }, () => {\n              'worklet'\n              _interactionState.value = InteractionState.RESET\n            })\n          }\n        } else {\n          // 其它情况，跟随手指滑动\n          this._translY.value = e.absoluteY - this._startY.value\n        }\n      }\n\n      if (e.state === GestureState.END || e.state === GestureState.CANCELLED) {\n        if (this._translY.value > 100) {\n          // 超过 100 就展开\n          this._interactionState.value = InteractionState.ANIMATING\n          this._translY.value = timing(this._mainHeight.value, { duration: 250 }, () => {\n            'worklet'\n            _interactionState.value = InteractionState.UNFOLD\n          })\n        } else if (this._translY.value > 0) {\n          // 没超过 100 但还在下拉状态就回弹\n          this._interactionState.value = InteractionState.ANIMATING\n          this._translY.value = timing(0.0, { duration: 250 }, () => {\n            'worklet'\n            _interactionState.value = InteractionState.INITIAL\n          })\n        } else if (this._translY.value > -this._tabsTop.value) {\n          // 往上滑就做滚动动画\n          this._interactionState.value = InteractionState.SCROLL\n          this._translY.value = decay({velocity: e.velocityY, clamp: [-this._tabsTop.value, 0]})\n        }\n      }\n    },\n    shouldPanResponse() {\n      'worklet'\n      return this._canPan.value\n    },\n    handleScroll(e) {\n      'worklet'\n      const _interactionState = this._interactionState\n      this._scrollTop.value = e.detail.scrollTop\n      if (this._scrollTop.value < 0 && !e.detail.isDrag && !this._canPan.value && this._interactionState.value !== InteractionState.ANIMATING) {\n        this._interactionState.value = InteractionState.ANIMATING\n        this._translY.value = timing(0.0, { duration: 250 }, () => {\n          'worklet'\n          _interactionState.value = InteractionState.INITIAL\n        })\n      }\n    },\n    shouldScrollViewResponse(e) {\n      'worklet'\n      if (this._translY.value > -this._tabsTop.value) {\n        this._canPan.value = true\n      } else {\n        // 触顶 && 往下拉时，pan 手势生效\n        this._canPan.value = this._scrollTop.value <= 0 && e.deltaY > 0\n      }\n      return !this._canPan.value\n    },\n    adjustDeceleration(velocity) {\n      'worklet'\n      const scrollTop = this._scrollTop.value\n      return scrollTop <= 0 ? 0 : velocity\n    },\n  },\n})\n"
  },
  {
    "path": "examples/expanded-scroll-view/pages/index/index.json",
    "content": "{\n  \"usingComponents\": {},\n  \"disableScroll\": true,\n  \"navigationStyle\": \"custom\",\n  \"renderer\": \"skyline\",\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/expanded-scroll-view/pages/index/index.wxml",
    "content": "<view class=\"navigation-bar\" style=\"padding-top: {{statusBarHeight}}px;\">\n  <view class=\"navigation-bar-content {{expand ? 'black' : 'white'}}\">\n    <view class=\"back\">ㄑ</view>\n    <view class=\"search\"><view class=\"search-input\">请输入课程名称</view></view>\n    <view class=\"more\"></view>\n  </view>\n</view>\n\n<pan-gesture-handler\n  tag=\"pan\"\n  simultaneousHandlers=\"{{['scroll-view']}}\"\n  shouldResponseOnMove=\"shouldPanResponse\"\n  onGestureEvent=\"handlePan\"\n>\n  <view class=\"page\">\n    <view class=\"header\">\n      <image class=\"header-banner\" src=\"https://res.wx.qq.com/op_res/6eQ8-k5PlDq7oIfHRTI1-ypWYiTfqCEMVGNUfVixvh1WCMXbQsPi5NSd9dlpzweXM_meCc7diXE5ZinaY-Fo-A\"></image>\n      <view class=\"header-shop-outer\">\n        <view class=\"header-shop-inner\">\n          <view class=\"header-shop-name\">微信学堂</view>\n          <view class=\"header-shop-info\">\n            <view class=\"header-shop-info-simple\">\n              <view class=\"header-shop-data\">\n                <text>⭐️ 5.0</text><text>66万人学过</text><text>好评度100%</text>\n              </view>\n              <!-- <view class=\"header-shop-tags\"><text>官方课程</text></view> -->\n              <view class=\"header-shop-coupon\">\n                <text>开发</text><text>行业</text><text>服务商</text>\n              </view>\n            </view>\n            <view class=\"header-shop-info-detail\">\n              <view class=\"header-shop-data\">\n                <text>⭐️ 5.0</text><text>66万人学过</text><text>好评度100%</text>\n              </view>\n              <view class=\"header-shop-tags\"><text>视频讲解、代码演示、文档一应俱全</text></view>\n              <view class=\"header-shop-coupon\">\n                <view class=\"title\">热门活动</view>\n                <view class=\"header-shop-coupon-item\">\n                  <view class=\"header-shop-coupon-discount\">开发</view>\n                  <text>小程序开发从入门到进阶</text>\n                </view>\n                <view class=\"header-shop-coupon-item\">\n                  <view class=\"header-shop-coupon-discount\">行业</view>\n                  <text>线上运营方法论</text>\n                </view>\n                <view class=\"header-shop-coupon-item\">\n                  <view class=\"header-shop-coupon-discount\">服务商</view>\n                  <text>如何成为微信服务商</text>\n                </view>\n              </view>\n              <view class=\"header-shop-announcement\">\n                <view class=\"title\">公告</view>\n                <view class=\"header-shop-announcement-content\">\n                  「微信学堂」已经上线了 70+ 门专题课程，支持小程序、公众号、企业微信、视频号等产品能力，帮助广大开发者和商家共同成长。<br/>\n                  商超零售、房地产、餐饮、酒旅、医疗... 「微信学堂」提供超过10个行业的经营教程。报名成为“微信小程序行业伙伴”，还有机会参加每月的闭门交流会，获取官方一手信息。\n                </view>\n              </view>\n            </view>\n          </view>\n        </view>\n      </view>\n    </view>\n    <view class=\"main\">\n      <view class=\"main-banner\">\n        <image mode=\"widthFix\" src=\"https://res.wx.qq.com/op_res/Gnr7xWUyNQyvf47WmPbKKCtaVNNyqiH61l5dcIBQUKifZeRA-fcA13QZ0IjqUZf7nUnj5ObWm7PusO8OIwyOug\" bind:load=\"onBannerLoaded\"></image>\n      </view>\n      <view class=\"tabs\">\n        <view class=\"tabs-list\">\n          <text>课程</text>\n          <text>评价</text>\n        </view>\n        <view class=\"tabs-indicator\"></view>\n      </view>\n      <view class=\"product\">\n        <view class=\"product-category\">\n          <view wx:for=\"{{categories}}\" wx:key=\"index\" class=\"product-category-item {{selected === index ? 'selected' : ''}}\">{{item}}</view>\n        </view>\n        <vertical-drag-gesture-handler\n          tag=\"scroll-view\"\n          native-view=\"scroll-view\"\n          simultaneousHandlers=\"{{['pan']}}\"\n          shouldResponseOnMove=\"shouldScrollViewResponse\"\n        >\n          <scroll-view\n            class=\"product-list\"\n            type=\"custom\"\n            scroll-y\n            show-scrollbar=\"{{false}}\"\n            adjustDecelerationVelocity=\"adjustDeceleration\"\n            worklet:onscrollupdate=\"handleScroll\"\n          >\n            <sticky-section wx:for=\"{{list}}\" wx:key=\"header\">\n              <sticky-header>\n                <view class=\"product-group\">{{item.header}}</view>\n              </sticky-header>\n              <list-view>\n                <view class=\"product-item\" wx:for=\"{{item.data}}\" wx:key=\"name\" wx:for-item=\"subItem\">\n                  <image class=\"product-image\" fade-in src=\"{{subItem.image}}\"></image>\n                  <view class=\"product-info\">\n                    <view class=\"product-name\">{{subItem.name}}</view>\n                    <view class=\"product-comment\"><text>{{subItem.comment}}</text></view>\n                    <view class=\"product-data\"><text>{{subItem.sales}}人学过</text><text>好评度100%</text></view>\n                    <span class=\"product-price\">\n                      <text style=\"font-size: 10px;\">¥</text>\n                      <text style=\"font-size: 16px; font-weight: bold;\">{{subItem.price}}</text>\n                      <text style=\"color: gray;\"> 起</text>\n                    </span>\n                  </view>\n                  <view class=\"product-add-to-cart\">+</view>\n                </view>\n              </list-view>\n            </sticky-section>\n          </scroll-view>\n        </vertical-drag-gesture-handler>\n      </view>\n    </view>\n  </view>\n</pan-gesture-handler>"
  },
  {
    "path": "examples/expanded-scroll-view/pages/index/index.wxss",
    "content": "view {\n  position: relative;\n  box-sizing: border-box;\n}\n\n.navigation-bar {\n  position: absolute;\n  z-index: 1;\n  top: 0;\n  width: 100%;\n  padding-top: env(safe-area-inset-top);\n  background-color: transparent;\n}\n.navigation-bar-content {\n  height: 44px;\n  display: flex;\n  flex-direction: row;\n  justify-content: center;\n  align-items: center;\n  font-size: 20px;\n}\n.navigation-bar-content.white {\n  color: white;\n}\n.navigation-bar-content.black {\n  color: black;\n}\n.navigation-bar-content .back, .navigation-bar-content .more {\n  width: 44px;\n  text-align: center;\n}\n.navigation-bar-content .search {\n  flex: 1;\n  margin-right: 60px;\n  margin-top: 7px;\n  align-self: flex-start;\n}\n.navigation-bar-content .search-input {\n  position: absolute;\n  right: 0;\n  width: 100%;\n  height: 30px;\n  line-height: 30px;\n  padding-left: 20px;\n  border-radius: 15px;\n  font-size: 12px;\n  background-color: #F8F8F8;\n  color: #AAA;\n  opacity: 0;\n}\n\n.page {\n  position: absolute;\n  display: flex;\n  flex-direction: column;\n  width: 100vw;\n  height: 150vh;\n}\n\n.header-banner {\n  position: absolute;\n  width: 100%;\n  height: 140px;\n  z-index: -1;\n}\n.header-shop-outer {\n  padding-top: calc(env(safe-area-inset-top) + 70px);\n  background-color: rgba(0, 0, 0, .5);\n}\n.header-shop-inner {\n  background-color: white;\n  border-top-left-radius: 10px;\n  border-top-right-radius: 10px;\n  padding: 10px 15px;\n  font-size: 12px;\n  line-height: 1.8;\n}\n.header-shop-name {\n  font-size: 20px;\n  font-weight: bold;\n  margin-bottom: 5px;\n}\n.header-shop-info-simple {\n  color: #333;\n}\n.header-shop-data {\n  display: flex;\n  flex-direction: row;\n  line-height: 1.2;\n  color: gray;\n  margin-bottom: 8px;\n}\n.header-shop-data text {\n  padding-right: 5px;\n}\n.header-shop-data text:first-child {\n  color: orange;\n}\n.header-shop-tags {\n  display: flex;\n  flex-direction: row;\n  margin-bottom: 5px;\n}\n.header-shop-tags text {\n  background-color: #EEE;\n  padding: 1px 3px;\n  border-radius: 3px;\n}\n.header-shop-info-simple .header-shop-coupon {\n  display: flex;\n  flex-direction: row;\n  margin-bottom: 5px;\n  color: red;\n}\n.header-shop-info-simple .header-shop-coupon text {\n  padding: 0 2px;\n  border: 0.5px solid red;\n  border-radius: 2px;\n  margin-right: 5px;\n}\n.header-shop-info-detail {\n  position: absolute;\n  top: 0;\n  width: 100%;\n  opacity: 0;\n  color: #333;\n}\n.header-shop-info-detail .header-shop-coupon,\n.header-shop-info-detail .header-shop-announcement {\n  margin-top: 20px;\n}\n.header-shop-info-detail .title {\n  font-size: 16px;\n  font-weight: bold;\n  margin-bottom: 5px;\n}\n.header-shop-coupon-item {\n  display: flex;\n  flex-direction: row;\n  margin-bottom: 6px;\n}\n.header-shop-coupon-item view {\n  background-color: orangered;\n  color: white;\n  font-weight: 500;\n  border-radius: 3px;\n  margin-right: 6px;\n  padding: 0 4px;\n}\n\n.main {\n  flex: 1;\n  overflow: hidden;\n  background-color: white;\n}\n\n.main-banner {\n  margin: 10px 15px;\n  border-radius: 8px;\n  /* height: 50px; */\n  /* background-color: burlywood; */\n}\n\n.main-banner image {\n  width: 100%;\n  border-radius: 8px;\n}\n\n.tabs {\n  border-bottom: 0.5px solid #EEE;\n}\n.tabs-list {\n  display: flex;\n  flex-direction: row;\n}\n.tabs-list text {\n  padding: 10px 15px;\n}\n.tabs-indicator {\n  position: absolute;\n  bottom: 0;\n  left: 0;\n  height: 2px;\n  width: 20px;\n  margin: 0 21px;\n  background-color: orange;\n}\n\n.product {\n  height: 100%;\n  display: flex;\n  flex-direction: row;\n}\n.product-category {\n  font-size: 12px;\n  background-color: #F9F9F9;\n}\n.product-category-item {\n  padding: 15px;\n}\n.product-category-item.selected {\n  background-color: white;\n}\n.product-list {\n  flex: 1;\n}\n.product-group {\n  font-size: 16px;\n  padding: 10px;\n  background-color: white;\n}\n.product-item {\n  display: flex;\n  flex-direction: row;\n  padding: 8px;\n}\n.product-image {\n  width: 133px;\n  height: 100px;\n  border-radius: 5px;\n}\n.product-info {\n  flex: 1;\n  padding: 0 8px;\n  font-size: 10px;\n}\n.product-info view {\n  margin-bottom: 3px;\n}\n.product-name {\n  font-size: 16px;\n}\n.product-comment {\n  display: flex;\n  flex-direction: row;\n}\n.product-comment text {\n  background-color: #EEE;\n  border-radius: 3px;\n  padding: 1px 3px;\n}\n.product-data {\n  display: flex;\n  flex-direction: row;\n  color: gray;\n}\n.product-data text {\n  margin-right: 20px;\n}\n.product-discount {\n  display: flex;\n  flex-direction: row;\n}\n.product-discount text {\n  color: red;\n  border: 0.3px solid red;\n  padding:  0 3px;\n  border-radius: 3px;\n}\n.product-price {\n  color: red;\n  vertical-align: baseline;\n}\n.product-add-to-cart {\n  position: absolute;\n  right: 12px;\n  bottom: 12px;\n  border-radius: 6px;\n  color: #fff;\n  background-color: #44b549;\n  width: 20px;\n  height: 20px;\n  line-height: 16px;\n  font-weight: 500;\n  text-align: center;\n}\n\nsticky-header {\n  position: sticky;\n  top: 0;\n  z-index: 1;\n  display: block;\n}\n"
  },
  {
    "path": "examples/expanded-scroll-view/project.config.json",
    "content": "{\n  \"compileType\": \"miniprogram\",\n  \"setting\": {\n    \"coverView\": true,\n    \"es6\": true,\n    \"postcss\": true,\n    \"minified\": true,\n    \"enhance\": true,\n    \"showShadowRootInWxmlPanel\": true,\n    \"packNpmRelationList\": [],\n    \"babelSetting\": {\n      \"ignore\": [],\n      \"disablePlugins\": [],\n      \"outputPath\": \"\"\n    },\n    \"condition\": false,\n    \"ignoreUploadUnusedFiles\": true,\n    \"compileWorklet\": true\n  },\n  \"condition\": {},\n  \"editorSetting\": {\n    \"tabIndent\": \"insertSpaces\",\n    \"tabSize\": 2\n  },\n  \"packOptions\": {\n    \"ignore\": [],\n    \"include\": []\n  },\n  \"appid\": \"wxe5f52902cf4de896\"\n}"
  },
  {
    "path": "examples/expanded-scroll-view/project.private.config.json",
    "content": "{\n  \"description\": \"项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档：https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html\",\n  \"projectname\": \"expanded-scroll-view\",\n  \"setting\": {\n    \"compileHotReLoad\": false,\n    \"skylineRenderEnable\": true\n  },\n  \"libVersion\": \"latest\"\n}"
  },
  {
    "path": "examples/expanded-scroll-view/sitemap.json",
    "content": "{\n    \"desc\": \"关于本文件的更多信息，请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html\",\n    \"rules\": [{\n    \"action\": \"allow\",\n    \"page\": \"*\"\n    }]\n}"
  },
  {
    "path": "examples/expanded-scroll-view/util.js",
    "content": "export function getCategory() {\n\tlet categorys = [\n\t  '中小商户',\n\t  '商超零售',\n\t  '品牌服饰',\n\t  '餐饮',\n\t  '医疗',\n\t  '酒旅',\n\t  '政务',\n\t  '开发技术',\n\t  '产品能力',\n\t  '运营规范',\n\t]\n\treturn categorys\n}\n\nexport function getProducts() {\n\tlet products = [\n\t\t'小程序性能优化课程',\n\t\t'小程序直播企业实践案例',\n\t\t'微信客服轻松配置，入门必修',\n\t\t'小程序如何帮助传统医院数字化？',\n\t\t'帮你快速掌握小商店经营秘诀',\n\t\t'了解小程序开发动态，听官方为你解读新能力',\n\t\t'快速了解微信小程序在医疗行业的应用',\n\t\t'解析常见小程序违规类型',\n\t\t'想做互联网的生意，可以通过微信怎么经营呢？',\n\t\t'政务行业小程序实践'\n\t]\n\tlet images = [\n\t\t'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU_LhYxhaP5JTy7TWgezsDY7RW_l_e04fR7oG7sCKmS8hc8mVeZaY6eUWT3nk-ww_ZQ',\n\t\t'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU-O3axOjUJGFgutF9Xc1JL1uxXFWYdW85mWG0Zvm5nv7rvP18CJ0q6-RRFM0xWLLog',\n\t\t'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU3ywQmrV-rSREDwo0Hp9m7iIZZ7Njvjq_TlOg_0ss0cgQL0pfKOuB2NRpAcwfALxvw',\n\t\t'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU1GROxmiPIBOCoA5Es44GxjN0KuCQQsoxEH33l05TCgk04n0dssHAIPxIV2ycSlSJA',\n\t\t'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU68wmBQzYcfQfuIAh1IKWq7OyG0EWxdWGhotYHFh-k-JpmkJ1Otq-mYUT8Dp3iucvg',\n\t\t'https://res.wx.qq.com/op_res/UxKgRAAdvQE0sTh7eCEwT1ASo_fbE3TppPoza_9I7U7OreGNv8F8ltq80gqQSzxa-86bt-gWalLtgPDAhflj-w',\n\t\t'https://res.wx.qq.com/op_res/UxKgRAAdvQE0sTh7eCEwT-SSHr1ULMcspj1yPw2dBQkxDV-Y_fOHodKNyHbS2JwBEVLnVKF2X_TPOhwZG9m0hQ',\n\t\t'https://res.wx.qq.com/op_res/Zmvv0fisUjaMjuqWLhWWkuzGktaXJEQt46EaKsCKeT06Z4tROseXN0joI7h2qwzqyx2FUy57cveZL-8iArI8_Q',\n\t\t'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJitcJIfp9P4VSWxi3126XeiyZ2BnnH0xg-oIXAUgHBgaHjBMwxzSjSkEkTMRqzlKZw',\n\t\t'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJnw5zq4f_XW3swKAowexqAbziuojU5W9v4CJixA-NDJShkfS0ne3KWY_6SB56yqb3g',\n\t]\n\tproducts = products.map((name, id) => ({\n\t\tname,\n\t\timage: images[(id % products.length)],\n\t\tcomment: '一如既往的好',\n\t\tsales: 6500,\n\t\tdiscount: 0.01,\n\t\tprice: 0.01\n\t}))\n\treturn products\n  }"
  },
  {
    "path": "examples/half-screen/.eslintrc.js",
    "content": "/*\n * Eslint config file\n * Documentation: https://eslint.org/docs/user-guide/configuring/\n * Install the Eslint extension before using this feature.\n */\nmodule.exports = {\n  env: {\n    es6: true,\n    browser: true,\n    node: true,\n  },\n  ecmaFeatures: {\n    modules: true,\n  },\n  parserOptions: {\n    ecmaVersion: 2018,\n    sourceType: 'module',\n  },\n  globals: {\n    wx: true,\n    App: true,\n    Page: true,\n    getCurrentPages: true,\n    getApp: true,\n    Component: true,\n    requirePlugin: true,\n    requireMiniProgram: true,\n  },\n  // extends: 'eslint:recommended',\n  rules: {},\n}\n"
  },
  {
    "path": "examples/half-screen/app.js",
    "content": "// app.js\nApp({})\n"
  },
  {
    "path": "examples/half-screen/app.json",
    "content": "{\n  \"pages\": [\n    \"pages/index/index\"\n  ],\n  \"window\": {\n    \"backgroundTextStyle\": \"light\",\n    \"navigationBarBackgroundColor\": \"#fff\",\n    \"navigationBarTitleText\": \"Weixin\",\n    \"navigationBarTextStyle\": \"black\"\n  },\n  \"style\": \"v2\",\n  \"renderer\": \"skyline\",\n  \"rendererOptions\": {\n    \"skyline\": {\n      \"defaultDisplayBlock\": true,\n      \"disableABTest\": true,\n      \"sdkVersionBegin\": \"3.0.0\",\n      \"sdkVersionEnd\": \"15.255.255\"\n    }\n  },\n  \"lazyCodeLoading\": \"requiredComponents\",\n  \"componentFramework\": \"glass-easel\",\n  \"sitemapLocation\": \"sitemap.json\"\n}"
  },
  {
    "path": "examples/half-screen/app.wxss",
    "content": ""
  },
  {
    "path": "examples/half-screen/pages/index/comment-data.js",
    "content": "// 获取留言列表\nexport function getCommentList() {\n  return [\n    {\n      \"comment\": \"为了进一步优化小程序性能，提供更为接近原生的用户体验，我们在 WebView 渲染之外新增了一个渲染引擎 Skyline，其使用更精简高效的渲染管线，并带来诸多增强特性，让 Skyline 拥有更接近原生渲染的性能体验。\",\n      \"userName\": \"binnie\",\n      \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n      \"subCommentList\": [\n        {\n          \"comment\": \"界面更不容易被逻辑阻塞，进一步减少卡顿。\",\n          \"userName\": \"拖拉机🚜\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/0AG3_hOKnGAqBhAhBx_a__0nu3Q_hGgnBQgiQhJMqZrvroKqdtYXhcSUdlp59bXjx7qF-ddTwGCcB-AqzYmlrw\",\n          \"replyUserName\": \"binnie\"\n        },\n        {\n          \"comment\": \"无需为每个页面新建一个 JS 引擎实例（WebView），减少了内存、时间开销。\",\n          \"userName\": \"binnie\",\n          \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n          \"replyUserName\": \"拖拉机🚜\",\n        },\n        {\n          \"comment\": \"框架可以在页面之间共享更多的资源，进一步减少运行时内存、时间开销。\",\n          \"userName\": \"拖拉机🚜\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/0AG3_hOKnGAqBhAhBx_a__0nu3Q_hGgnBQgiQhJMqZrvroKqdtYXhcSUdlp59bXjx7qF-ddTwGCcB-AqzYmlrw\",\n          \"replyUserName\": \"binnie\"\n        },\n        {\n          \"comment\": \"框架的代码之间无需再通过 JSBridge 进行数据交换，减少了大量通信时间开销。\",\n          \"userName\": \"binnie\",\n          \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n          \"replyUserName\": \"拖拉机🚜\",\n        }\n      ]\n    },\n    {\n      \"comment\": \"Skyline 以性能为首要目标，因此特性上在满足基本需求的前提下进行了大幅精简，目前 Skyline 只实现了 CSS 特性的子集。在编码上，Skyline 与 WebView 模式保持一致，仍使用 WXML 和 WXSS 编写界面。在不采用 Skyline 新增特性的情况下，适配了 Skyline 的小程序在低版本或未支持 Skyline 的平台上可无缝自动退回到 WebView 渲染。\",\n      \"userName\": \"拖拉机🚜\",\n      \"userHeadImg\": \"https://res.wx.qq.com/op_res/0AG3_hOKnGAqBhAhBx_a__0nu3Q_hGgnBQgiQhJMqZrvroKqdtYXhcSUdlp59bXjx7qF-ddTwGCcB-AqzYmlrw\",\n      \"subCommentList\": [\n        {\n          \"comment\": \"基于 Worklet 机制的 动画模块，能够在渲染线程同步运行动画相关逻辑。\",\n          \"userName\": \"拖拉机🚜\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/0AG3_hOKnGAqBhAhBx_a__0nu3Q_hGgnBQgiQhJMqZrvroKqdtYXhcSUdlp59bXjx7qF-ddTwGCcB-AqzYmlrw\",\n          \"replyUserName\": \"binnie\"\n        },\n        {\n          \"comment\": \"基于 Worklet 机制的 手势系统。在渲染线程同步监听手势、执行手势相关逻辑；支持手势协商处理；\",\n          \"userName\": \"小苹果🍎\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJBAK7KJg9KId_6N1-rJ4OyCCQoJGVMOaTabboo2viRucoxkPvHRkn2fVl6tectlzBg\",\n          \"replyUserName\": \"binnie\"\n        },\n        {\n          \"comment\": \"基于 Worklet 机制的 自定义路由模块，支持实现自定义路由动画和交互。\",\n          \"userName\": \"流星雨\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYf3q0W302-kseD8VxLKoItZ6HgneLkgpQSEMIgEKz_xVE7putZxs2YEYqB13Uh37_w\",\n          \"replyUserName\": \"binnie\"\n        },\n        {\n          \"comment\": \"支持 跨页面共享元素，能够将上一个页面的元素“共享”到下一个页面，并伴随着过渡动画。\",\n          \"userName\": \"落日余晖\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYYjda9Dp372N3T05q_nn3PgvoXBoReXvaXBfkthtXQLN7m5_YI6FoTre-xvJBDFLMA\",\n          \"replyUserName\": \"binnie\"\n        },\n      ]\n    },\n    {\n      \"comment\": \"Skyline 能很好地保持和原有架构的兼容性，基于 WebView 环境的小程序代码基本上无需任何改动即可直接在新的架构下运行。\",\n      \"userName\": \"小苹果🍎\",\n      \"userHeadImg\": \"https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJBAK7KJg9KId_6N1-rJ4OyCCQoJGVMOaTabboo2viRucoxkPvHRkn2fVl6tectlzBg\",\n      \"subCommentList\": []\n    },\n    {\n      \"comment\": \"Skyline 支持了一些 Web 所缺失的但很重要的能力，以满足开发者实现更好的交互体验。\",\n      \"userName\": \"binnie\",\n      \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n      \"subCommentList\": [\n\n      ]\n    },\n    {\n      \"comment\": \"worklet 动画可以做到类原生动画般的体验。\",\n      \"userName\": \"流星雨\",\n      \"userHeadImg\": \"https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYf3q0W302-kseD8VxLKoItZ6HgneLkgpQSEMIgEKz_xVE7putZxs2YEYqB13Uh37_w\",\n      \"subCommentList\": [\n        {\n          \"comment\": \"提供如 timing、spring 等常见动画方式的封装方法，开发者可自定义动画曲线，同时可对不同的动画类型进行组合、重复，形成交织动画。😄\",\n          \"userName\": \"binnie\",\n          \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n          \"replyUserName\": \"流星雨\"\n        }\n      ]\n    },\n    {\n      \"comment\": \"Skyline 中 wxs 代码运行在 AppService 线程，而事件产生在 UI 线程，因此 wxs 动画 性能有所降低，为了提升小程序交互体验的效果，我们内置了一批手势组件。\",\n      \"userName\": \"落日余晖\",\n      \"userHeadImg\": \"https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYYjda9Dp372N3T05q_nn3PgvoXBoReXvaXBfkthtXQLN7m5_YI6FoTre-xvJBDFLMA\",\n      \"subCommentList\": [\n        {\n          \"comment\": \"免去开发者监听 touch 事件，自行计算手势逻辑的复杂步骤；\",\n          \"userName\": \"binnie\",\n          \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n          \"replyUserName\": \"落日余晖\"\n        },\n        {\n          \"comment\": \"手势组件直接在 UI 线程响应，避免了传递到 JS 线程带来的延迟；\",\n          \"userName\": \"落日余晖\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYYjda9Dp372N3T05q_nn3PgvoXBoReXvaXBfkthtXQLN7m5_YI6FoTre-xvJBDFLMA\",\n          \"replyUserName\": \"binnie\"\n        }\n      ]\n    },\n    {\n      \"comment\": \"在连续的 Skyline 页面间跳转时，可实现自定义路由效果，路由动画的曲线、时长均可交由开发者控制。\",\n      \"userName\": \"binnie\",\n      \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n      \"subCommentList\": []\n    },\n    {\n      \"comment\": \"在连续的两个 Skyline 页面跳转时，可以将上一个页面的元素“共享”到下一个页面，并伴随着过渡动画。\",\n      \"userName\": \"绿意盎然\",\n      \"userHeadImg\": \"https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJH2f0R4uXyqnNGlrivO8cKbn0nz1DE_6s22rc91zluwIrqiAVZNREvCeVYAUS8aaZw\",\n      \"subCommentList\": []\n    },\n  ]\n}\n"
  },
  {
    "path": "examples/half-screen/pages/index/index.js",
    "content": "import { getCommentList } from \"./comment-data\"\n\nconst { shared, timing } = wx.worklet\n\nconst GestureState = {\n  POSSIBLE: 0, // 0 此时手势未识别，如 panDown等\n  BEGIN: 1, // 1 手势已识别\n  ACTIVE: 2, // 2 连续手势活跃状态\n  END: 3, // 3 手势终止\n  CANCELLED: 4, // 4 手势取消，\n}\n\nComponent({\n  data: {\n    list: getCommentList(),\n  },\n  lifetimes: {\n    created() {\n      this.transY = shared(1000)\n      this.scrollTop = shared(0)\n      this.startPan = shared(true)\n      this.commentHeight = shared(1000)\n    },\n    ready() {\n      const query = this.createSelectorQuery()\n      // ready 生命周期里才能获取到首屏的布局信息\n      query.select('.comment-container').boundingClientRect()\n      query.exec((res) => {\n        this.transY.value = this.commentHeight.value = res[0].height\n      })\n      // 通过 transY 一个 SharedValue 控制半屏的位置\n      this.applyAnimatedStyle('.comment-container', () => {\n        'worklet'\n        return { transform: `translateY(${this.transY.value}px)` }\n      })\n    },\n  },\n  methods: {\n    onTapOpenComment() {\n      this.openComment(300)\n    },\n    openComment(duration) {\n      'worklet'\n      this.transY.value = timing(0, { duration })\n    },\n    onTapCloseComment() {\n      this.closeComment()\n    },\n    closeComment() {\n      'worklet'\n      this.transY.value = timing(this.commentHeight.value, { duration: 200 })\n    },\n    // shouldPanResponse 和 shouldScrollViewResponse 用于 pan 手势和 scroll-view 滚动手势的协商\n    shouldPanResponse() {\n      'worklet'\n      return this.startPan.value\n    },\n    shouldScrollViewResponse(pointerEvent) {\n      'worklet'\n      // transY > 0 说明 pan 手势在移动半屏，此时滚动不应生效\n      if (this.transY.value > 0) return false\n      const scrollTop = this.scrollTop.value\n      const { deltaY } = pointerEvent\n      // deltaY > 0 是往上滚动，scrollTop <= 0 是滚动到顶部边界，此时 pan 开始生效，滚动不生效\n      const result = scrollTop <= 0 && deltaY > 0\n      this.startPan.value = result\n      return !result\n    },\n    handlePan(gestureEvent) {\n      'worklet'\n      if (gestureEvent.state === GestureState.ACTIVE) {\n        const curPosition = this.transY.value\n        const destination = Math.max(0, curPosition + gestureEvent.deltaY)\n        if (curPosition === destination) return\n        this.transY.value = destination\n      }\n\n      if (gestureEvent.state === GestureState.END || gestureEvent.state === GestureState.CANCELLED) {\n        if (gestureEvent.velocityY > 500 && this.transY.value > 50) {\n          this.closeComment()\n        } else if (this.transY.value > this.commentHeight.value / 2) {\n          this.closeComment()\n        } else {\n          this.openComment(100)\n        }\n      }\n    },\n    adjustDecelerationVelocity(velocity) {\n      'worklet'\n      const scrollTop = this.scrollTop.value\n      return scrollTop <= 0 ? 0 : velocity\n    },\n    handleScroll(evt) {\n      'worklet'\n      this.scrollTop.value = evt.detail.scrollTop\n    },\n  },\n})\n"
  },
  {
    "path": "examples/half-screen/pages/index/index.json",
    "content": "{\n  \"usingComponents\": {},\n  \"disableScroll\": true,\n  \"navigationStyle\": \"custom\"\n}"
  },
  {
    "path": "examples/half-screen/pages/index/index.wxml",
    "content": "<scroll-view class=\"container\" scroll-y type=\"list\">\n  <image src=\"https://res.wx.qq.com/op_res/FAyreGAUWqLJv09oZqfNbsZz1a4HO_JEUy5rIcjJrlKaGrOcbjL6i6BvGd8snx2csU2JI0UwcVZGE48Tzvf9aQ\" mode=\"widthFix\"></image>\n</scroll-view>\n<view class=\"open-comment\" bind:tap=\"onTapOpenComment\">\n  <view class=\"open-comment-wording\">打开留言</view>\n  <view class=\"safe-area-inset-bottom\"></view>\n</view>\n\n<view class=\"comment-container\">\n  <!-- 顶部不参与手势协商，单独控制 -->\n  <pan-gesture-handler worklet:ongesture=\"handlePan\" style=\"flex-shrink: 0;\">\n    <view class=\"comment-header\">\n      <view class=\"close-comment\" bind:tap=\"onTapCloseComment\">∨</view>\n      留言\n    </view>\n  </pan-gesture-handler>\n  <!-- 滚动区要与 pan 手势协商 -->\n  <pan-gesture-handler id=\"pan\" worklet:should-response-on-move=\"shouldPanResponse\" simultaneous-handlers=\"{{['scroll']}}\" worklet:ongesture=\"handlePan\">\n    <vertical-drag-gesture-handler id=\"scroll\" native-view=\"scroll-view\" worklet:should-response-on-move=\"shouldScrollViewResponse\" simultaneous-handlers=\"{{['pan']}}\">\n      <scroll-view class=\"comment-list\" scroll-y worklet:adjust-deceleration-velocity=\"adjustDecelerationVelocity\" worklet:onscrollupdate=\"handleScroll\" type=\"list\" show-scrollbar=\"{{false}}\">\n        <view class=\"comment-item\" wx:for=\"{{list}}\" wx:key=\"comment\">\n          <view class=\"main-comment\">\n            <image fade-in class=\"user-head-img\" src=\"{{item.userHeadImg}}\"></image>\n            <view class=\"others\">\n              <text class=\"user-name\">{{item.userName}}</text>\n              <text class=\"content\">{{item.comment}}</text>\n            </view>\n          </view>\n          <view class=\"sub-comment\" wx:for=\"{{item.subCommentList}}\" wx:key=\"comment\" wx:for-item=\"subItem\" wx:for-index=\"subIndex\">\n            <image fade-in class=\"user-head-img\" src=\"{{subItem.userHeadImg}}\"></image>\n            <view class=\"others\">\n              <text class=\"user-name\">{{subItem.userName}} 回复 {{subItem.replyUserName}}</text>\n              <text class=\"content\">{{subItem.comment}}</text>\n            </view>\n          </view>\n        </view>\n        <view class=\"safe-area-inset-bottom\"></view>\n      </scroll-view>\n    </vertical-drag-gesture-handler>\n  </pan-gesture-handler>\n</view>\n"
  },
  {
    "path": "examples/half-screen/pages/index/index.wxss",
    "content": "page {\n  display: flex;\n  flex-direction: column;\n  padding-top: env(safe-area-inset-top);\n  width: 100vw;\n  height: 100vh;\n  color: #1A191E;\n}\npage, view {\n  box-sizing: border-box;\n}\npan-gesture-handler, vertical-drag-gesture-handler {\n  display: flex;\n  flex-direction: column;\n  overflow: hidden;\n}\n.container {\n  flex: 1;\n  width: 100vw;\n  min-height: auto;\n  overflow: hidden;\n}\n.container image {\n  width: 100vw;\n}\n\n.open-comment {\n  display: flex;\n  flex-direction: column;\n  flex-shrink: 0;\n  width: 100%;\n  background-color: white;\n}\n.open-comment-wording {\n  height: 66px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n.safe-area-inset-bottom {\n  height: env(safe-area-inset-bottom);\n}\n\n.comment-container {\n  width: 100vw;\n  height: 70vh;\n  display: flex;\n  flex-direction: column;\n  position: absolute;\n  bottom: 0;\n  z-index: 999;\n  background-color: white;\n  border-top-left-radius: 20px;\n  border-top-right-radius: 20px;\n  box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.2);\n  transform: translateY(100%);\n}\n.comment-header {\n  width: 100%;\n  font-size: 16px;\n  text-align: center;\n  padding: 15px 0;\n}\n.close-comment {\n  position: absolute;\n  left: 20px;\n  font-size: 10px;\n  font-weight: bold;\n  background-color: #F8F8F8;\n  border-radius: 100%;\n  width: 20px;\n  height: 20px;\n  line-height: 20px;\n  z-index: 1;\n}\n.comment-list {\n  flex: 1;\n  overflow: hidden;\n}\n.comment-item {\n  padding: 0 20px 20px;\n  font-size: 13px;\n  line-height: 1.4;\n}\n.main-comment, .sub-comment {\n  display: flex;\n  flex-direction: row;\n}\n.sub-comment {\n  padding: 10px 22px 0;\n}\n\n.user-head-img {\n  width: 33px;\n  height: 33px;\n  border-radius: 50%;\n  margin-top: 5px;\n}\n.others {\n  flex: 1;\n  margin-left: 10px;\n}\n.user-name {\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n.content {\n  margin-top: 2px;\n}\n"
  },
  {
    "path": "examples/half-screen/project.config.json",
    "content": "{\n  \"appid\": \"wxe5f52902cf4de896\",\n  \"compileType\": \"miniprogram\",\n  \"libVersion\": \"latest\",\n  \"packOptions\": {\n    \"ignore\": [],\n    \"include\": []\n  },\n  \"setting\": {\n    \"coverView\": true,\n    \"es6\": true,\n    \"postcss\": true,\n    \"minified\": true,\n    \"enhance\": true,\n    \"showShadowRootInWxmlPanel\": true,\n    \"packNpmRelationList\": [],\n    \"babelSetting\": {\n      \"ignore\": [],\n      \"disablePlugins\": [],\n      \"outputPath\": \"\"\n    },\n    \"condition\": false,\n    \"skylineRenderEnable\": true,\n    \"compileWorklet\": true\n  },\n  \"condition\": {},\n  \"editorSetting\": {\n    \"tabIndent\": \"insertSpaces\",\n    \"tabSize\": 2\n  }\n}"
  },
  {
    "path": "examples/half-screen/project.private.config.json",
    "content": "{\n  \"description\": \"项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档：https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html\",\n  \"projectname\": \"half-screen\",\n  \"setting\": {\n    \"compileHotReLoad\": false,\n    \"skylineRenderEnable\": true\n  },\n  \"libVersion\": \"latest\"\n}"
  },
  {
    "path": "examples/half-screen/sitemap.json",
    "content": "{\n    \"desc\": \"关于本文件的更多信息，请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html\",\n    \"rules\": [{\n    \"action\": \"allow\",\n    \"page\": \"*\"\n    }]\n}"
  },
  {
    "path": "examples/product-list/.eslintrc.js",
    "content": "/*\n * Eslint config file\n * Documentation: https://eslint.org/docs/user-guide/configuring/\n * Install the Eslint extension before using this feature.\n */\nmodule.exports = {\n  env: {\n    es6: true,\n    browser: true,\n    node: true,\n  },\n  ecmaFeatures: {\n    modules: true,\n  },\n  parserOptions: {\n    ecmaVersion: 2018,\n    sourceType: 'module',\n  },\n  globals: {\n    wx: true,\n    App: true,\n    Page: true,\n    getCurrentPages: true,\n    getApp: true,\n    Component: true,\n    requirePlugin: true,\n    requireMiniProgram: true,\n  },\n  // extends: 'eslint:recommended',\n  rules: {},\n}\n"
  },
  {
    "path": "examples/product-list/app.js",
    "content": "// app.js\nApp({})\n"
  },
  {
    "path": "examples/product-list/app.json",
    "content": "{\n  \"pages\": [\n      \"pages/index/index\"\n  ],\n  \"window\": {\n    \"backgroundTextStyle\": \"light\",\n    \"navigationBarBackgroundColor\": \"#fff\",\n    \"navigationBarTitleText\": \"Weixin\",\n    \"navigationBarTextStyle\": \"black\"\n  },\n  \"style\": \"v2\",\n  \"renderer\": \"skyline\",\n  \"rendererOptions\": {\n    \"skyline\": {\n      \"defaultDisplayBlock\": true,\n      \"disableABTest\": true,\n      \"sdkVersionBegin\": \"3.0.0\",\n      \"sdkVersionEnd\": \"15.255.255\"\n    }\n  },\n  \"lazyCodeLoading\": \"requiredComponents\",\n  \"componentFramework\": \"glass-easel\",\n  \"sitemapLocation\": \"sitemap.json\"\n}"
  },
  {
    "path": "examples/product-list/app.wxss",
    "content": "/**app.wxss**/\nview {\n  box-sizing: border-box;\n}"
  },
  {
    "path": "examples/product-list/components/navigation-bar/index.js",
    "content": "Component({\n  options: {\n    multipleSlots: true // 在组件定义时的选项中启用多slot支持\n  },\n  /**\n   * 组件的属性列表\n   */\n  properties: {\n    extClass: {\n      type: String,\n      value: ''\n    },\n    title: {\n      type: String,\n      value: ''\n    },\n    background: {\n      type: String,\n      value: ''\n    },\n    color: {\n      type: String,\n      value: ''\n    },\n    back: {\n      type: Boolean,\n      value: true\n    },\n    loading: {\n      type: Boolean,\n      value: false\n    },\n    animated: {\n      // 显示隐藏的时候opacity动画效果\n      type: Boolean,\n      value: true\n    },\n    show: {\n      // 显示隐藏导航，隐藏的时候navigation-bar的高度占位还在\n      type: Boolean,\n      value: true,\n      observer: '_showChange'\n    },\n    // back为true的时候，返回的页面深度\n    delta: {\n      type: Number,\n      value: 1\n    }\n  },\n  /**\n   * 组件的初始数据\n   */\n  data: {\n    displayStyle: ''\n  },\n  attached() {\n    const rect = wx.getMenuButtonBoundingClientRect()\n    wx.getSystemInfo({\n      success: (res) => {\n        this.setData({\n          statusBarHeight: res.statusBarHeight,\n          innerPaddingRight: `padding-right:${res.windowWidth - rect.left}px`,\n          leftWidth: `width:${res.windowWidth - rect.left}px`,\n          navBarHeight: rect.bottom + rect.top - res.statusBarHeight,\n        })\n      }\n    })\n  },\n  /**\n   * 组件的方法列表\n   */\n  methods: {\n    _showChange(show) {\n      const animated = this.data.animated\n      let displayStyle = ''\n      if (animated) {\n        displayStyle = `opacity: ${show ? '1' : '0'};transition: opacity 0.5s;`\n      } else {\n        displayStyle = `display: ${show ? '' : 'none'}`\n      }\n      this.setData({\n        displayStyle\n      })\n    },\n    back() {\n      const data = this.data\n      if (data.delta) {\n        wx.navigateBack({\n          delta: data.delta\n        })\n      }\n      this.triggerEvent('back', { delta: data.delta }, {})\n    }\n  }\n})\n"
  },
  {
    "path": "examples/product-list/components/navigation-bar/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {},\n  \"componentFramework\": \"glass-easel\",\n  \"addGlobalClass\": true\n}"
  },
  {
    "path": "examples/product-list/components/navigation-bar/index.wxml",
    "content": "<view class=\"weui-navigation-bar {{extClass}}\">\n  <view class=\"weui-navigation-bar__inner\" style=\"padding-top: {{statusBarHeight}}px; height: {{navBarHeight}}px; color: {{color}}; background: {{background}}; {{displayStyle}}; {{innerPaddingRight}};\">\n    <view class='weui-navigation-bar__left' style=\"{{back ? leftWidth: ''}}\">\n      <block wx:if=\"{{back}}\">\n        <view class=\"weui-navigation-bar__buttons\">\n          <view bindtap=\"back\" class=\"weui-navigation-bar__btn_goback_wrapper\" hover-class=\"weui-active\" aria-role=\"button\" aria-label=\"返回\">\n            <view class=\"weui-navigation-bar__button weui-navigation-bar__btn_goback\"></view>\n          </view>\n        </view>\n      </block>\n      <block wx:else>\n        <slot name=\"left\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__center'>\n      <view wx:if=\"{{loading}}\" class=\"weui-navigation-bar__loading\" aria-role=\"alert\">\n        <view class=\"weui-loading\" style=\"width:{{size.width}}rpx;height:{{size.height}}rpx;\" aria-role=\"img\" aria-label=\"加载中\"></view>\n      </view>\n      <block wx:if=\"{{title}}\">\n        <text>{{title}}</text>\n      </block>\n      <block wx:else>\n        <slot name=\"center\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__right'>\n      <slot name=\"right\"></slot>\n    </view>\n  </view>\n</view>\n"
  },
  {
    "path": "examples/product-list/components/navigation-bar/index.wxss",
    "content": ".weui-navigation-bar {\n  overflow: hidden;\n  color: rgba(0, 0, 0, .9);\n  width: 100vw;\n}\n\n.weui-navigation-bar__placeholder {\n  background: #f7f7f7;\n  position: relative;\n}\n\n.weui-navigation-bar__inner, .weui-navigation-bar__inner .weui-navigation-bar__left {\n  display: flex;\n  align-items: center;\n  flex-direction: row;\n}\n\n.weui-navigation-bar__inner {\n  position: relative;\n  padding-right: 95px;\n  width: 100vw;\n  box-sizing: border-box;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left {\n  position: relative;\n  /* width: 95px; */\n  padding-left: 16px;\n  box-sizing: border-box;\n}\n\n.weui-navigation-bar__btn_goback_wrapper {\n  padding: 11px 18px 11px 16px;\n  margin: -11px -18px -11px -16px;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left .weui-navigation-bar__btn_goback {\n  font-size: 12px;\n  width: 12px;\n  height: 24px;\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E\") no-repeat 50% 50%;\n  background-size: cover;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__center {\n  font-size: 17px;\n  text-align: center;\n  position: relative;\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-weight: bold;\n}\n\n@media(prefers-color-scheme: dark) {\n  .weui-navigation-bar {\n    color: hsla(0, 0%, 100%, .8);\n  }\n  .weui-navigation-bar__inner {\n    background-color: #1f1f1f;\n  }\n}\n"
  },
  {
    "path": "examples/product-list/pages/index/index.js",
    "content": "import { getCategory, getGoods, getVIPCategory } from '../../util'\n\nconst systemInfo = wx.getSystemInfoSync()\n\nconst { shared, Easing } = wx.worklet\n\nconst lerp = function (begin, end, t) {\n  'worklet'\n  return begin + (end - begin) * t\n}\n\nconst clamp = function (cur, lowerBound, upperBound) {\n  'worklet'\n  if (cur > upperBound) return upperBound\n  if (cur < lowerBound) return lowerBound\n  return cur\n}\n\nComponent({\n  data: {\n    goods: getGoods(30),\n    categorySet: [{\n      page: 0,\n      categorys: getCategory()\n    }, {\n      page: 1,\n      categorys: getCategory().reverse()\n    }],\n    paddingTop: 44,\n    renderer: 'skyline',\n    vipCategorys: getVIPCategory(),\n    categoryItemWidth: 0,\n    intoView: '',\n    selected: 0\n  },\n\n  lifetimes: {\n    created() {\n      this.searchBarWidth = shared(100)\n      this.navBarOpactiy = shared(1)\n    },\n    attached() {\n      const padding = 10 * 2\n      const categoryItemWidth = (systemInfo.windowWidth - padding) / 5\n      this.setData({ categoryItemWidth, paddingTop: systemInfo.statusBarHeight, renderer: this.renderer })\n\n      this.applyAnimatedStyle('.nav-bar', () => {\n        'worklet'\n        return {\n          opacity: this.navBarOpactiy.value\n        }\n      })\n\n      this.applyAnimatedStyle('.search', () => {\n        'worklet'\n        return {\n          width: `${this.searchBarWidth.value}%`,\n        }\n      })\n\n      this.applyAnimatedStyle('.search-container', () => {\n        'worklet'\n        return {\n          backgroundColor: (this.navBarOpactiy.value > 0 && this.renderer == 'skyline') ? 'transparent' : '#fff'\n        }\n      })\n    },\n  },\n\n  methods: {\n    chooseVipCategory(evt) {\n      const id = evt.currentTarget.dataset.id\n      this.setData({\n        intoView: `vip-category-${id}`,\n        selected: parseInt(id, 10)\n      })\n    },\n\n    handleScrollUpdate(evt) {\n      'worklet'\n      const maxDistance = 60\n      const scrollTop = clamp(evt.detail.scrollTop, 0, maxDistance)\n      const progress = scrollTop / maxDistance\n      const EasingFn = Easing.cubicBezier(0.4, 0.0, 0.2, 1.0)\n      this.searchBarWidth.value = lerp(100, 70, EasingFn(progress))\n      this.navBarOpactiy.value = lerp(1, 0, progress)\n    },\n  },\n})\n"
  },
  {
    "path": "examples/product-list/pages/index/index.json",
    "content": "{\n  \"usingComponents\": {\n    \"navigation-bar\": \"../../components/navigation-bar\"\n  },\n  \"disableScroll\": true,\n  \"navigationStyle\": \"custom\"\n}\n"
  },
  {
    "path": "examples/product-list/pages/index/index.wxml",
    "content": "<navigation-bar class=\"nav-bar\" title=\"\" back=\"{{false}}\">\n  <view class=\"nav-left\" slot=\"left\">\n    <image class=\"nav-logo\" mode=\"aspectFill\" src=\"https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU5TS9E0E2WBIJqkwvK4DV_NdciupZQ58P62ReribRjp3Cn0uqFLZ6v4DC7719-YaeA\" />\n    <view class=\"nav-title\">\n      <text>微信学堂</text>\n    </view>\n  </view>\n</navigation-bar>\n\n<scroll-view\n class=\"scroll-area\"\n type=\"custom\"\n scroll-y\n show-scrollbar=\"{{false}}\"\n worklet:onscrollupdate=\"handleScrollUpdate\"\n>\n  <view class=\"fake-nav-bar\" />\n  <sticky-section push-pinned-header=\"{{false}}\">\n    <sticky-header>\n      <view class=\"search-container\" style=\"{{renderer == 'skyline' ? 'padding' : 'margin'}}-top: {{paddingTop}}px;\">\n        <view class=\"search\">\n          <view class=\"search-icon-wrp\">\n            <image class=\"search-icon\" src=\"/images/search.png\" />\n          </view>\n          <view class=\"search-text\">这是skyline实现的～</view>\n          <view class=\"search-btn\">搜索</view>\n        </view>\n      </view>\n    </sticky-header>\n    <swiper class=\"category-wrp\">\n      <block wx:for=\"{{categorySet}}\" wx:key=\"page\">\n        <swiper-item>\n          <view class=\"category-list\">\n            <block wx:for=\"{{item.categorys}}\" wx:key=\"id\" wx:for-item=\"category\">\n              <view class=\"category-item\" style=\"width: {{categoryItemWidth}}px;\">\n                <image class=\"category-icon\" src=\"{{category.icon}}\" />\n                <view class=\"category-name\">\n                  <text>{{category.name}}</text>\n                </view>\n              </view>\n            </block>\n          </view>\n        </swiper-item>\n      </block>\n    </swiper>\n  </sticky-section>\n\n  <sticky-section push-pinned-header=\"{{false}}\">\n    <sticky-header style=\"position: sticky; top: 0; z-index: 1; display: block;\">\n      <scroll-view\n       class=\"vip-categorys-list\"\n       scroll-x\n       type=\"list\"\n       show-scrollbar=\"{{false}}\"\n       enable-flex\n       scroll-with-animation\n       scroll-into-view=\"{{intoView}}\"\n       scroll-into-view-alignment=\"center\"\n      >\n        <block wx:for=\"{{vipCategorys}}\" wx:key=\"id\">\n          <view class=\"vip-category-item\" id=\"vip-category-{{index}}\">\n            <text class=\"vip-category-name {{selected === index ? 'selected' : ''}}\" data-id=\"{{index}}\" bind:tap=\"chooseVipCategory\">{{item.name}}</text>\n          </view>\n        </block>\n      </scroll-view>\n    </sticky-header>\n    <grid-view\n     padding=\"{{[6, 6, 6, 6]}}\"\n     cross-axis-gap=\"6\"\n     main-axis-gap=\"6\"\n     cross-axis-count=\"2\"\n     type=\"masonry\"\n    >\n      <block wx:for=\"{{goods}}\" wx:key=\"id\">\n        <view class=\"good\">\n          <image mode=\"widthFix\" class=\"good-icon\" fade-in src=\"{{item.icon}}\" />\n          <view class=\"good-title\">\n            <text>{{item.title}}</text>\n          </view>\n          <view class=\"good-comment\">\n            <text>3万+评价</text>\n          </view>\n        </view>\n      </block>\n    </grid-view>\n  </sticky-section>\n</scroll-view>\n"
  },
  {
    "path": "examples/product-list/pages/index/index.wxss",
    "content": ".fake-nav-bar {\n  height: 60px;\n}\n\n.search-container {\n  padding: 0 16px 10px 16px;\n  /* margin-top: 44px; */\n  /* background-color: transparent; */\n  background-color: #FFF;\n}\n.search {\n  display: flex;\n  flex-direction: row;\n  box-sizing: border-box;\n  width: 100%;\n  height: 40px;\n  border-radius: 20px;\n  border: 2px solid #07c160;\n  position: relative;\n  align-items: center;\n  background-color: #fff;\n}\n\n.search-text {\n  color: #8f8888;\n  font-size: 14px;\n}\n\n.search-icon-wrp {\n  display: flex;\n  width: 30px;\n  height: 100%;\n  flex-direction: row;\n  align-items: center;\n  justify-content: center;\n}\n\n.search-icon {\n  width: 16px;\n  height: 16px;\n}\n\n.search-btn {\n  position: absolute;\n  right: 0;\n  width: 60px;\n  height: 100%;\n  border-radius: 20px;\n  background-color: #07c160;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #FFF;\n  font-size: 16px;\n  /* font-weight: bold; */\n}\n\n.nav-bar {\n  background-color: #fff;\n  position: absolute;\n}\n\n.nav-left {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n}\n\n.nav-logo {\n  width: 40px;\n  height: 40px;\n  border-radius: 50%;\n}\n\n.nav-title {\n  margin-left: 2px;\n  font-size: 20px;\n  color: #3f3e3e;\n  /* font-weight: bold; */\n}\n\n.scroll-area {\n  height: 100vh;\n}\n\n.category-wrp {\n  height: 220px;\n  padding: 10px 0;\n  background-color: #FFF;\n}\n\n.category-list {\n  display: flex;\n  flex-direction: row;\n  justify-content: space-between;\n  flex-wrap: wrap;\n  padding: 0 10px;\n}\n\n.category-item {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  margin: 10px 0;\n  font-size: 14px;\n}\n\n.category-icon {\n  width: 50px;\n  height: 50px;\n  margin-bottom: 10px;\n  border-radius: 50%;\n}\n\n.category-name {\n  height: 30px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.good {\n  background-color: #FFF;\n  border-radius: 6px;\n  overflow: hidden;\n}\n\n.good-icon {\n  width: 100%;\n}\n\n.good-title {\n  width: 100%;\n  line-height: 1.4;\n  margin: 8px 0;\n  padding: 0 5px;\n  font-size: 14px;\n  box-sizing: border-box;\n}\n\n.good-comment {\n  font-size: 12px;\n  color: #ccc;\n  padding-left: 5px;\n  margin-bottom: 10px;\n}\n\n.vip-categorys-list {\n  background-color: #fff;\n  width: 100%;\n  height: 60px;\n  display: flex;\n  flex-direction: row;\n}\n\n.vip-category-item {\n  display: flex;\n  flex-shrink: 0;\n  height: 100%;\n  justify-content: center;\n  align-items: center;\n  padding-right: 20px;\n  padding-left: 40px;\n}\n.vip-category-item:first-child {\n  padding-left: 20px;\n}\n\n.vip-category-name {\n  color: #8f8888;\n  font-size: 16px;\n  transition: transform .3s;\n}\n\n.selected {\n  transform: scale(1.2);\n  color: #2c2c2c;\n  font-weight: bold;\n}\n"
  },
  {
    "path": "examples/product-list/project.config.json",
    "content": "{\n  \"appid\": \"wxe5f52902cf4de896\",\n  \"compileType\": \"miniprogram\",\n  \"libVersion\": \"latest\",\n  \"packOptions\": {\n    \"ignore\": [],\n    \"include\": []\n  },\n  \"setting\": {\n    \"coverView\": true,\n    \"es6\": true,\n    \"postcss\": true,\n    \"minified\": true,\n    \"enhance\": true,\n    \"showShadowRootInWxmlPanel\": true,\n    \"packNpmRelationList\": [],\n    \"babelSetting\": {\n      \"ignore\": [],\n      \"disablePlugins\": [],\n      \"outputPath\": \"\"\n    },\n    \"condition\": false,\n    \"skylineRenderEnable\": true,\n    \"compileWorklet\": true\n  },\n  \"condition\": {},\n  \"editorSetting\": {\n    \"tabIndent\": \"insertSpaces\",\n    \"tabSize\": 2\n  }\n}"
  },
  {
    "path": "examples/product-list/project.private.config.json",
    "content": "{\n  \"description\": \"项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档：https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html\",\n  \"projectname\": \"product-list\",\n  \"setting\": {\n    \"compileHotReLoad\": false\n  },\n  \"libVersion\": \"latest\"\n}"
  },
  {
    "path": "examples/product-list/sitemap.json",
    "content": "{\n    \"desc\": \"关于本文件的更多信息，请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html\",\n    \"rules\": [{\n    \"action\": \"allow\",\n    \"page\": \"*\"\n    }]\n}"
  },
  {
    "path": "examples/product-list/util.js",
    "content": "export function getCategory() {\n  let categorys = [\n    '中小商户',\n    '商超零售',\n    '品牌服饰',\n    '餐饮',\n    '医疗',\n    '酒旅',\n    '政务',\n    '开发技术',\n    '产品能力',\n    '运营规范',\n  ]\n  let images = [\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJgc9If4xjgvN3O4UQclWMiJxMoExkarf71FN-3SSf3Sh-GoatfvTbKcPE-grH-1L9g',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJrmxgZKLSYHHwqx6YfJyqPnSNeIHovelr_r6GLFpsiCuCuBgYKBc68vBi0dJYSMeZA',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJk4e_uP2-FWEQKo5Ijp5itIrlf-qIXozTGY6D595Ri2YIoLCUS7YseOda2JLTAEz7Q',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJj6M4Aqf4ov3F7tRlrb62now5owS_Q6vkhsWjnU_uWVbBR84dTHxG4tzAcjwAqOGZQ',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJrBCMpypwJ_xFgSnas6etb7Y6JuMRRMBJ6cSMmbmSkCkOjCSPDdC_eLEK1_FT-d-PQ',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJitcJIfp9P4VSWxi3126XeiyZ2BnnH0xg-oIXAUgHBgaHjBMwxzSjSkEkTMRqzlKZw',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJnw5zq4f_XW3swKAowexqAbziuojU5W9v4CJixA-NDJShkfS0ne3KWY_6SB56yqb3g',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJu8Q1L10fFiMMnZVYnLoP1GuT0q26CJLtjSRfJAjTvj6DBNuWrzzMD9UYZEb-pznKA',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChUzvBckq_FnOrUIIgSP2hJ9cYByUIwgzBEaDCcF5YiPNMmcMg0ewQBn_nMCt-q71vsg',\n    'https://res.wx.qq.com/op_res/Zmvv0fisUjaMjuqWLhWWkuzGktaXJEQt46EaKsCKeT06Z4tROseXN0joI7h2qwzqyx2FUy57cveZL-8iArI8_Q',\n  ]\n  categorys = categorys.map((name, id) => ({\n    id,\n    name,\n    icon: images[(id % categorys.length)] //`/images/boy/b${id}.png`\n  }))\n  return categorys\n}\n\nexport function getGoods(num) {\n  const titles = [\n    '小程序性能优化课程基于实际开发场景，提升小程序性能表现，满足用户体验',\n    '解析常见小程序违规类型，帮助大家更好理解平台规则',\n    '快速了解微信小程序在医疗行业的应用',\n    '小程序直播的企业实践案例。',\n    '微信客服轻松配置，入门必修',\n    '想做互联网的生意，可以通过微信怎么经营呢？',\n    '了解小程序开发动态，听官方为你解读新能力',\n    '医保支付、互联网医院、线上问诊...小程序如何帮助传统医院数字化？',\n    '内含开店指引、店铺运营和平台规则，帮你快速掌握小商店经营秘诀',\n    '浅谈连锁零售的私域流量运营'\n  ]\n\n  const images = [\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU_LhYxhaP5JTy7TWgezsDY7RW_l_e04fR7oG7sCKmS8hc8mVeZaY6eUWT3nk-ww_ZQ',\n    'https://res.wx.qq.com/op_res/Zmvv0fisUjaMjuqWLhWWkuzGktaXJEQt46EaKsCKeT06Z4tROseXN0joI7h2qwzqyx2FUy57cveZL-8iArI8_Q',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJrBCMpypwJ_xFgSnas6etb7Y6JuMRRMBJ6cSMmbmSkCkOjCSPDdC_eLEK1_FT-d-PQ',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU-O3axOjUJGFgutF9Xc1JL1uxXFWYdW85mWG0Zvm5nv7rvP18CJ0q6-RRFM0xWLLog',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU3ywQmrV-rSREDwo0Hp9m7iIZZ7Njvjq_TlOg_0ss0cgQL0pfKOuB2NRpAcwfALxvw',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJgc9If4xjgvN3O4UQclWMiJxMoExkarf71FN-3SSf3Sh-GoatfvTbKcPE-grH-1L9g',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJu8Q1L10fFiMMnZVYnLoP1GuT0q26CJLtjSRfJAjTvj6DBNuWrzzMD9UYZEb-pznKA',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU1GROxmiPIBOCoA5Es44GxjN0KuCQQsoxEH33l05TCgk04n0dssHAIPxIV2ycSlSJA',\n    'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU68wmBQzYcfQfuIAh1IKWq7OyG0EWxdWGhotYHFh-k-JpmkJ1Otq-mYUT8Dp3iucvg',\n    'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJrmxgZKLSYHHwqx6YfJyqPnSNeIHovelr_r6GLFpsiCuCuBgYKBc68vBi0dJYSMeZA'\n  ]\n\n  const goods = []\n  for (let id = 0; id < num; id++) {\n    goods.push({\n      id,\n      title: titles[(id % titles.length)],\n      icon: images[(id % titles.length)] // `/images/goods/g${(id % num)}.jpg`\n    })\n  }\n  return goods\n}\n\nexport function getVIPCategory() {\n  let vipCategorys = [\n    '本月最热',\n    '官方经营',\n    '行业实践',\n    '微信服务商'\n  ]\n  vipCategorys = vipCategorys.map((name, id) => ({\n    id,\n    name\n  }))\n  return vipCategorys\n}"
  },
  {
    "path": "examples/refresher-two-level/app.js",
    "content": "// app.js\nApp({})\n"
  },
  {
    "path": "examples/refresher-two-level/app.json",
    "content": "{\n\t\"pages\": [\n\t\t\"index/index\"\n\t],\n\t\"window\": {\n\t  \"backgroundTextStyle\": \"light\",\n\t  \"navigationBarBackgroundColor\": \"#fff\",\n\t  \"navigationBarTitleText\": \"Weixin\",\n\t  \"navigationBarTextStyle\": \"black\"\n\t},\n\t\"style\": \"v2\",\n\t\"renderer\": \"skyline\",\n\t\"rendererOptions\": {\n\t  \"skyline\": {\n\t\t\"defaultDisplayBlock\": true,\n\t\t\"disableABTest\": true,\n\t\t\"sdkVersionBegin\": \"3.0.0\",\n\t\t\"sdkVersionEnd\": \"15.255.255\"\n\t  }\n\t},\n\t\"lazyCodeLoading\": \"requiredComponents\",\n\t\"componentFramework\": \"glass-easel\",\n\t\"sitemapLocation\": \"sitemap.json\"\n  }"
  },
  {
    "path": "examples/refresher-two-level/app.wxss",
    "content": ""
  },
  {
    "path": "examples/refresher-two-level/components/navigation-bar/index.js",
    "content": "Component({\n  options: {\n    multipleSlots: true // 在组件定义时的选项中启用多slot支持\n  },\n  /**\n   * 组件的属性列表\n   */\n  properties: {\n    extClass: {\n      type: String,\n      value: ''\n    },\n    title: {\n      type: String,\n      value: ''\n    },\n    background: {\n      type: String,\n      value: ''\n    },\n    color: {\n      type: String,\n      value: ''\n    },\n    back: {\n      type: Boolean,\n      value: true\n    },\n    loading: {\n      type: Boolean,\n      value: false\n    },\n    animated: {\n      // 显示隐藏的时候opacity动画效果\n      type: Boolean,\n      value: true\n    },\n    show: {\n      // 显示隐藏导航，隐藏的时候navigation-bar的高度占位还在\n      type: Boolean,\n      value: true,\n      observer: '_showChange'\n    },\n    // back为true的时候，返回的页面深度\n    delta: {\n      type: Number,\n      value: 1\n    }\n  },\n  /**\n   * 组件的初始数据\n   */\n  data: {\n    displayStyle: ''\n  },\n  attached() {\n    const rect = wx.getMenuButtonBoundingClientRect()\n    wx.getSystemInfo({\n      success: (res) => {\n        this.setData({\n          statusBarHeight: res.statusBarHeight,\n          innerPaddingRight: `padding-right:${res.windowWidth - rect.left}px`,\n          leftWidth: `width:${res.windowWidth - rect.left}px`,\n          navBarHeight: rect.bottom + rect.top - res.statusBarHeight,\n        })\n      }\n    })\n  },\n  /**\n   * 组件的方法列表\n   */\n  methods: {\n    _showChange(show) {\n      const animated = this.data.animated\n      let displayStyle = ''\n      if (animated) {\n        displayStyle = `opacity: ${show ? '1' : '0'};transition: opacity 0.5s;`\n      } else {\n        displayStyle = `display: ${show ? '' : 'none'}`\n      }\n      this.setData({\n        displayStyle\n      })\n    },\n    back() {\n      const data = this.data\n      if (data.delta) {\n        wx.navigateBack({\n          delta: data.delta\n        })\n      }\n      this.triggerEvent('back', { delta: data.delta }, {})\n    }\n  }\n})\n"
  },
  {
    "path": "examples/refresher-two-level/components/navigation-bar/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {},\n  \"componentFramework\": \"glass-easel\",\n  \"addGlobalClass\": true\n}"
  },
  {
    "path": "examples/refresher-two-level/components/navigation-bar/index.wxml",
    "content": "<view class=\"weui-navigation-bar {{extClass}}\">\n  <view class=\"weui-navigation-bar__inner\" style=\"padding-top: {{statusBarHeight}}px; height: {{navBarHeight}}px; color: {{color}}; background: {{background}}; {{displayStyle}}; {{innerPaddingRight}};\">\n    <view class='weui-navigation-bar__left' style=\"{{back ? leftWidth: ''}}\">\n      <block wx:if=\"{{back}}\">\n        <view class=\"weui-navigation-bar__buttons\">\n          <view bindtap=\"back\" class=\"weui-navigation-bar__btn_goback_wrapper\" hover-class=\"weui-active\" aria-role=\"button\" aria-label=\"返回\">\n            <view class=\"weui-navigation-bar__button weui-navigation-bar__btn_goback\"></view>\n          </view>\n        </view>\n      </block>\n      <block wx:else>\n        <slot name=\"left\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__center'>\n      <view wx:if=\"{{loading}}\" class=\"weui-navigation-bar__loading\" aria-role=\"alert\">\n        <view class=\"weui-loading\" style=\"width:{{size.width}}rpx;height:{{size.height}}rpx;\" aria-role=\"img\" aria-label=\"加载中\"></view>\n      </view>\n      <block wx:if=\"{{title}}\">\n        <text>{{title}}</text>\n      </block>\n      <block wx:else>\n        <slot name=\"center\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__right'>\n      <slot name=\"right\"></slot>\n    </view>\n  </view>\n</view>\n"
  },
  {
    "path": "examples/refresher-two-level/components/navigation-bar/index.wxss",
    "content": ".weui-navigation-bar {\n  overflow: hidden;\n  color: rgba(0, 0, 0, .9);\n  width: 100vw;\n}\n\n.weui-navigation-bar__placeholder {\n  background: #f7f7f7;\n  position: relative;\n}\n\n.weui-navigation-bar__inner, .weui-navigation-bar__inner .weui-navigation-bar__left {\n  display: flex;\n  align-items: center;\n  flex-direction: row;\n}\n\n.weui-navigation-bar__inner {\n  position: relative;\n  padding-right: 95px;\n  width: 100vw;\n  box-sizing: border-box;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left {\n  position: relative;\n  /* width: 95px; */\n  padding-left: 16px;\n  box-sizing: border-box;\n}\n\n.weui-navigation-bar__btn_goback_wrapper {\n  padding: 11px 18px 11px 16px;\n  margin: -11px -18px -11px -16px;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left .weui-navigation-bar__btn_goback {\n  font-size: 12px;\n  width: 12px;\n  height: 24px;\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E\") no-repeat 50% 50%;\n  background-size: cover;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__center {\n  font-size: 17px;\n  text-align: center;\n  position: relative;\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-weight: bold;\n}\n\n@media(prefers-color-scheme: dark) {\n  .weui-navigation-bar {\n    color: hsla(0, 0%, 100%, .8);\n  }\n  .weui-navigation-bar__inner {\n    background-color: #1f1f1f;\n  }\n}\n"
  },
  {
    "path": "examples/refresher-two-level/goods/index.json",
    "content": "{\n  \"usingComponents\": {\n    \"navigation-bar\": \"../components/navigation-bar\"\n  },\n  \"renderer\": \"skyline\",\n  \"componentFramework\": \"glass-easel\"\n}"
  },
  {
    "path": "examples/refresher-two-level/goods/index.less",
    "content": "@import '../goods.less';"
  },
  {
    "path": "examples/refresher-two-level/goods/index.ts",
    "content": "import { getCategory, getGoods, getVIPCategory, getExpCategory, getVideoList } from '../util'\n\n// pages/goods/index.ts\nPage({\n\n  /**\n   * 页面的初始数据\n   */\n  data: {\n    goodsData: {\n      expSelected: 0,\n      expCategorys: getExpCategory(),\n      videoList: getVideoList(20),\n      hasRouteDone: false,\n    }\n  },\n\n  back() {\n    wx.navigateBack({})\n  },\n\n  /**\n   * 生命周期函数--监听页面加载\n   */\n\n  onRouteDone() {\n    console.info('@@@ goods page routeDone ')\n    this.setData({\n      'goodsData.hasRouteDone': true,\n    })\n    if (this.eventChannel) {\n      this.eventChannel.emit('nextPageRouteDone', { });\n    }\n  },\n\n  onLoad() {\n    this.eventChannel = this.getOpenerEventChannel()\n  },\n\n  /**\n   * 生命周期函数--监听页面初次渲染完成\n   */\n  onReady() {\n\n  },\n\n  /**\n   * 生命周期函数--监听页面显示\n   */\n  onShow() {\n\n  },\n\n  /**\n   * 生命周期函数--监听页面隐藏\n   */\n  onHide() {\n\n  },\n\n  /**\n   * 生命周期函数--监听页面卸载\n   */\n  onUnload() {\n\n  },\n\n  /**\n   * 页面相关事件处理函数--监听用户下拉动作\n   */\n  onPullDownRefresh() {\n\n  },\n\n  /**\n   * 页面上拉触底事件的处理函数\n   */\n  onReachBottom() {\n\n  },\n\n  /**\n   * 用户点击右上角分享\n   */\n  onShareAppMessage() {\n\n  }\n})"
  },
  {
    "path": "examples/refresher-two-level/goods/index.wxml",
    "content": "<import src=\"../goods.wxml\" />\n\n<template is=\"goods\" data=\"{{...goodsData}}\" />"
  },
  {
    "path": "examples/refresher-two-level/goods.less",
    "content": ".exp-room {\n\t.nav-bar {\n\t  background-color: #fff;\n\t  position: absolute;\n\t}\n\t\n\t.nav-left {\n\t  display: flex;\n\t  flex-direction: row;\n\t  align-items: center;\n\t}\n\t\n\t.nav-logo {\n\t  width: 40px;\n\t  height: 40px;\n\t  border-radius: 50%;\n\t}\n\t\n\t.nav-title {\n\t  margin-left: 2px;\n\t  font-size: 20px;\n\t  color: #3f3e3e;\n\t}\n\t\n\twidth: 100vw;\n\theight: 100vh;\n\tbackground-color: rgb(3, 3, 41);\n  \n\t.nav-bar {\n\t  position: relative;\n\t  background-color: rgb(3, 3, 41);\n\t}\n  \n\t.nav-logo {\n\t  width: 32px;\n\t  height: 32px;\n\t}\n  \n\t.nav-title {\n\t  color: #fff;\n\t  font-size: 18px;\n\t}\n  \n\t.exp-category-list {\n\t  display: flex;\n\t  flex-direction: row;\n\t  margin: 10px 16px;\n\t}\n  \n\t.exp-category-item {\n\t  margin-right: 20px;\n\t}\n  \n\t.exp-category-name {\n\t  color: #8f8888;\n\t  font-size: 16px;\n\t}\n  \n\t.selected {\n\t  transform: scale(1.2);\n\t  color: #fff;\n\t  font-weight: bold;\n\t}\n  \n\t.scroll-area {\n\t  flex: 1;\n\t}\n  \n\t.video-container {\n\t  height: 240px;\n\t}\n  \n\t.video {\n\t  overflow: hidden;\n\t  border-radius: 16px;\n\t  margin: 10px 0;\n\t  position: relative;\n\t  width: 100%;\n\t  height: 100%;\n\t}\n  \n\t.video-title {\n\t  position: absolute;\n\t  top: 16px;\n\t  left: 16px;\n\t  color: #fff;\n\t  font-size: 14px;\n\t}\n  \n\t.expand {\n\t  width: 100%;\n\t  height: 100%;\n\t}\n  \n\t.refresher-tips {\n\t  position: absolute;\n\t  bottom: 20px;\n\t  width: 100%;\n\t  text-align: center;\n\t  color: #fff;\n\t}\n  }\n  "
  },
  {
    "path": "examples/refresher-two-level/goods.wxml",
    "content": "<template name=\"goods\">\n\t<view class=\"exp-room\">\n\t\t<view class=\"expand\">\n\t\t\t<navigation-bar class=\"nav-bar\" title=\"\" back=\"{{false}}\">\n\t\t\t\t<view class=\"nav-left\" slot=\"left\" bind:tap=\"back\">\n\t\t\t\t\t<image class=\"nav-logo\" mode=\"aspectFill\" src=\"../images/back_delete.png\" />\n\t\t\t\t\t<view class=\"nav-title\">\n\t\t\t\t\t\t<text>官方讲师课程</text>\n\t\t\t\t\t</view>\n\t\t\t\t</view>\n\t\t\t</navigation-bar>\n\t\t\t<block wx:if=\"{{hasRouteDone}}\">\n\t\t\t\t<view class=\"exp-category-list\">\n\t\t\t\t\t<block wx:for=\"{{expCategorys}}\" wx:key=\"id\">\n\t\t\t\t\t\t<view class=\"exp-category-item\">\n\t\t\t\t\t\t\t<text class=\"exp-category-name {{expSelected === index ? 'selected' : ''}}\">{{item.name}}</text>\n\t\t\t\t\t\t</view>\n\t\t\t\t\t</block>\n\t\t\t\t</view>\n\t\t\t\t<scroll-view\n\t\t\t\t class=\"scroll-area\"\n\t\t\t\t type=\"list\"\n\t\t\t\t scroll-y\n\t\t\t\t padding=\"{{padding}}\"\n\t\t\t\t show-scrollbar=\"{{false}}\"\n\t\t\t\t>\n\t\t\t\t\t<block wx:for=\"{{videoList}}\" wx:key=\"id\">\n\t\t\t\t\t\t<view class=\"video-container\">\n\t\t\t\t\t\t\t<view class=\"video\" style=\"background-color: green;\">\n\t\t\t\t\t\t\t\t<image style=\"width: 100%;height: 100%;\" mode=\"aspectFill\" src=\"{{item.url}}\"></image>\n\t\t\t\t\t\t\t</view>\n\t\t\t\t\t\t</view>\n\t\t\t\t\t</block>\n\t\t\t\t</scroll-view>\n\t\t\t</block>\n\t\t</view>\n\t</view>\n</template>\n\n"
  },
  {
    "path": "examples/refresher-two-level/index/index.json",
    "content": "{\n  \"usingComponents\": {\n    \"navigation-bar\": \"../components/navigation-bar\"\n  },\n  \"disableScroll\": true,\n  \"renderer\": \"skyline\",\n  \"navigationStyle\": \"custom\",\n  \"componentFramework\": \"glass-easel\"\n}\n"
  },
  {
    "path": "examples/refresher-two-level/index/index.less",
    "content": "@import '../goods.less';\n\n.fake-nav-bar {\n  height: 60px;\n}\n\n.search-container {\n  padding: 0 16px 10px 16px;\n  /* margin-top: 44px; */\n  /* background-color: transparent; */\n  background-color: #FFF;\n}\n\n.search {\n  display: flex;\n  flex-direction: row;\n  box-sizing: border-box;\n  width: 100%;\n  height: 40px;\n  border-radius: 20px;\n  border: 2px solid #07c160;\n  position: relative;\n  align-items: center;\n  background-color: #fff;\n}\n\n.search-text {\n  color: #8f8888;\n  font-size: 14px;\n}\n\n.search-icon-wrp {\n  display: flex;\n  width: 30px;\n  height: 100%;\n  flex-direction: row;\n  align-items: center;\n  justify-content: center;\n}\n\n.search-icon {\n  width: 16px;\n  height: 16px;\n}\n\n.search-btn {\n  position: absolute;\n  right: 0;\n  width: 60px;\n  height: 100%;\n  border-radius: 20px;\n  background-color: #07c160;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: #FFF;\n  font-size: 16px;\n  /* font-weight: bold; */\n}\n\n.nav-bar {\n  background-color: #fff;\n  position: absolute;\n}\n\n.nav-left {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n}\n\n.nav-logo {\n  width: 40px;\n  height: 40px;\n  border-radius: 50%;\n}\n\n.nav-title {\n  margin-left: 2px;\n  font-size: 20px;\n  color: #3f3e3e;\n  /* font-weight: bold; */\n}\n\n.scroll-area {\n  height: 100vh;\n}\n\n.category-wrp {\n  height: 220px;\n  padding: 10px 0;\n  background-color: #FFF;\n}\n\n.category-list {\n  display: flex;\n  flex-direction: row;\n  justify-content: space-between;\n  flex-wrap: wrap;\n  padding: 0 10px;\n}\n\n.category-item {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  margin: 10px 0;\n  font-size: 14px;\n}\n\n.category-icon {\n  width: 50px;\n  height: 50px;\n  margin-bottom: 10px;\n  border-radius: 50%;\n}\n\n.category-name {\n  height: 30px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.good {\n  background-color: #FFF;\n}\n\n.good-icon {\n  width: 100%;\n  border-radius: 6px;\n}\n\n.good-title {\n  width: 100%;\n  line-height: 1.4;\n  margin: 8px 0;\n  padding: 0 5px;\n  font-size: 14px;\n}\n\n.good-comment {\n  font-size: 12px;\n  color: #ccc;\n  padding-left: 5px;\n  margin-bottom: 10px;\n}\n\n.vip-categorys-list {\n  background-color: #fff;\n  width: 100%;\n  height: 60px;\n}\n\n.vip-category-item {\n  display: flex;\n  height: 100%;\n  justify-content: center;\n  padding-right: 20px;\n}\n\n.vip-category-name {\n  color: #8f8888;\n  font-size: 16px;\n  transition: transform .3s;\n}\n\n.selected {\n  transform: scale(1.2);\n  color: #2c2c2c;\n  font-weight: bold;\n}"
  },
  {
    "path": "examples/refresher-two-level/index/index.ts",
    "content": "import { getCategory, getGoods, getVIPCategory, getExpCategory, getVideoList } from '../util'\n\nexport enum RefreshStatus {\n  Idle,\n  CanRefresh,\n  Refreshing,\n  Completed,\n  Failed,\n  CanTwoLevel,\n  TwoLevelOpening,\n  TwoLeveling,\n  TwoLevelClosing,\n}\n\nconst systemInfo = wx.getSystemInfoSync()\n\nconst { shared, Easing } = wx.worklet\n\nconst EasingFn = Easing.cubicBezier(0.4, 0.0, 0.2, 1.0)\n\nconst lerp = function (begin, end, t) {\n  'worklet'\n  return begin + (end - begin) * t\n}\n\nconst clamp = function (cur, lowerBound, upperBound) {\n  'worklet'\n  if (cur > upperBound) return upperBound\n  if (cur < lowerBound) return lowerBound\n  return cur\n}\n\nconst secondFloorCover = 'https://res.wx.qq.com/op_res/cgyy76UKHo3KhkAvZN0WW8YtThK1-Kf4a21kiEQxNAvea-eN8S8tcbsLcdAIISVQeFM_E7sR-wtDb2VP5_Cy1w'\n\n\nComponent({\n  data: {\n    goods: getGoods(30),\n    categorySet: [{\n      page: 0,\n      categorys: getCategory()\n    }, {\n      page: 1,\n      categorys: getCategory().reverse()\n    }],\n    paddingTop: 44,\n    renderer: 'skyline',\n    vipCategorys: getVIPCategory(),\n    categoryItemWidth: 0,\n    intoView: '',\n    selected: 0,\n    padding: [0, 16, 0, 16],\n    triggered: false,\n    twoLevelTriggered: false,\n    isTwoLevel: false,\n    refreshStatus: '下拉刷新',\n    secondFloorCover,\n    goodsData: {\n      expSelected: 0,\n      expCategorys: getExpCategory(),\n      videoList: getVideoList(20),\n      hasRouteDone: true,\n    }\n  },\n\n  lifetimes: {\n    created() {\n      this.searchBarWidth = shared(100)\n      this.navBarOpactiy = shared(1)\n    },\n    attached() {\n      const padding = 10 * 2\n      const categoryItemWidth = (systemInfo.windowWidth - padding) / 5\n      this.setData({ categoryItemWidth, paddingTop: systemInfo.statusBarHeight, renderer: this.renderer })\n\n      this.applyAnimatedStyle('.nav-bar', () => {\n        'worklet'\n        return {\n          opacity: this.navBarOpactiy.value\n        }\n      })\n\n      this.applyAnimatedStyle('.search', () => {\n        'worklet'\n        return {\n          width: `${this.searchBarWidth.value}%`,\n        }\n      })\n\n      this.applyAnimatedStyle('.search-container', () => {\n        'worklet'\n        console.log('111', this.renderer)\n        return {\n          backgroundColor: (this.navBarOpactiy.value > 0 && this.renderer == 'skyline') ? 'transparent' : '#fff'\n        }\n      })\n\n      wx.createSelectorQuery()\n      .select('#scrollview')\n      .node()\n      .exec((res) => {\n        console.info('@@@ createSelectorQuery ', res)\n        this.scrollContext = res[0].node;\n      })\n    },\n  },\n\n  methods: {\n\n    chooseVipCategory(evt) {\n      const id = evt.currentTarget.dataset.id\n      this.setData({\n        intoView: `vip-category-${id}`,\n        selected: parseInt(id, 10)\n      })\n    },\n\n    handleScrollStart(evt) {\n      'worklet'\n\n    },\n\n    handleScrollUpdate(evt) {\n      'worklet'\n      const maxDistance = 60\n      const scrollTop = clamp(evt.detail.scrollTop, 0, maxDistance)\n      const progress = EasingFn(scrollTop / maxDistance)\n      this.searchBarWidth.value = lerp(100, 70, progress)\n      this.navBarOpactiy.value = lerp(1, 0, progress)\n    },\n\n    handleScrollEnd(evt) {\n      'worklet'\n    },\n\n    onPulling(e) {\n      // console.log('onPulling:', e)\n    },\n  \n    onRefresh() {\n      console.info('@@@ onRefresh')\n      if (this._freshing) return\n      this._freshing = true\n      setTimeout(() => {\n        this.setData({\n          triggered: false,\n        })\n        this._freshing = false\n      }, 2000)\n    },\n  \n    onRestore(e) {\n      console.log('onRestore:', e)\n    },\n  \n    onAbort(e) {\n      console.log('onAbort', e)\n    },\n\n    closeTwoLevel() {\n      this.setData({\n        twoLevelTriggered: false,\n      })\n    },\n\n    onStatusChange(e) {\n      const status: RefreshStatus = e.detail.status\n      const twoLevelModes = [RefreshStatus.TwoLevelOpening, RefreshStatus.TwoLeveling, RefreshStatus.TwoLevelClosing]\n      const isTwoLevel = twoLevelModes.indexOf(status) >= 0\n      const refreshStatus = this.buildText(status)\n      this.setData({\n        isTwoLevel,\n        refreshStatus,\n      })\n\n      if (status === RefreshStatus.TwoLeveling) {\n        const that = this\n        wx.navigateTo({\n          url: '../goods/index',\n          events: {\n            nextPageRouteDone: function(data) {\n              console.info('@@@ next page has routeDone')\n              that.scrollContext.closeTwoLevel({\n                duration: 1\n              })\n            }\n          },\n          success(res) {}\n        })\n      }\n    },\n\n    buildText(status: RefreshStatus) {\n      switch (status) {\n        case RefreshStatus.Idle:\n          return '下拉刷新'\n        case RefreshStatus.CanRefresh:\n          return '松手刷新，下拉进入二楼'\n        case RefreshStatus.Refreshing:\n          return '正在刷新'\n        case RefreshStatus.Completed:\n          return '刷新成功'\n        case RefreshStatus.Failed:\n          return '刷新失败'\n        case RefreshStatus.CanTwoLevel:\n          return '松手进入二楼'\n        default:\n          return '进入二楼'\n      }\n    },\n  },\n})\n"
  },
  {
    "path": "examples/refresher-two-level/index/index.wxml",
    "content": "<import src=\"../goods.wxml\" />\n<navigation-bar class=\"nav-bar\" title=\"\" back=\"{{false}}\">\n\t<view class=\"nav-left\" slot=\"left\">\n\t\t<image class=\"nav-logo\" mode=\"aspectFill\" src=\"https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU5TS9E0E2WBIJqkwvK4DV_NdciupZQ58P62ReribRjp3Cn0uqFLZ6v4DC7719-YaeA\" />\n\t\t<view class=\"nav-title\">\n\t\t\t<text>微信学堂</text>\n\t\t</view>\n\t</view>\n</navigation-bar>\n\n<scroll-view\n id=\"scrollview\"\n class=\"scroll-area\"\n type=\"custom\"\n scroll-y\n show-scrollbar=\"{{false}}\"\n refresher-enabled=\"{{true}}\"\n refresher-threshold=\"{{80}}\"\n refresher-default-style=\"none\"\n refresher-triggered=\"{{triggered}}\"\n refresher-two-level-pinned\t=\"{{false}}\"\n refresher-two-level-enabled\n refresher-two-level-threshold=\"{{150.0}}\"\n refresher-two-level-triggered=\"{{twoLevelTriggered}}\"\n bind:refresherpulling=\"onPulling\"\n bind:refresherrefresh=\"onRefresh\"\n bind:refresherrestore=\"onRestore\"\n bind:refresherabort=\"onAbort\"\n bind:refresherstatuschange=\"onStatusChange\"\n worklet:onstartstart=\"handleScrollStart\"\n worklet:onscrollupdate=\"handleScrollUpdate\"\n worklet:onscrollend=\"handleScrollEnd\"\n>\n\t<view slot=\"refresher\" class=\"exp-room\">\n\t\t<view class=\"expand\">\n\t\t\t<image class=\"expand\" src=\"{{secondFloorCover}}\" />\n\t\t\t<view class=\"refresher-tips\">{{refreshStatus}}</view>\n\t\t</view>\n\t</view>\n\t<view class=\"fake-nav-bar\" />\n\t<sticky-section push-pinned-header=\"{{false}}\">\n\t\t<sticky-header>\n\t\t\t<view bind:tap=\"jumpNextPage\" class=\"search-container\" style=\"padding-top: {{paddingTop}}px;\">\n\t\t\t\t<view class=\"search\">\n\t\t\t\t\t<view class=\"search-icon-wrp\">\n\t\t\t\t\t\t<image class=\"search-icon\" src=\"../images/search.png\" />\n\t\t\t\t\t</view>\n\t\t\t\t\t<view class=\"search-text\">这是skyline实现的～{{twoLevelStyle}}</view>\n\t\t\t\t\t<view class=\"search-btn\">搜索</view>\n\t\t\t\t</view>\n\t\t\t</view>\n\t\t</sticky-header>\n\t\t<swiper class=\"category-wrp\">\n\t\t\t<block wx:for=\"{{categorySet}}\" wx:key=\"page\">\n\t\t\t\t<swiper-item>\n\t\t\t\t\t<view class=\"category-list\">\n\t\t\t\t\t\t<block wx:for=\"{{item.categorys}}\" wx:key=\"id\" wx:for-item=\"category\">\n\t\t\t\t\t\t\t<view class=\"category-item\" style=\"width: {{categoryItemWidth}}px;\">\n\t\t\t\t\t\t\t\t<image class=\"category-icon\" src=\"{{category.icon}}\" />\n\t\t\t\t\t\t\t\t<view class=\"category-name\">\n\t\t\t\t\t\t\t\t\t<text>{{category.name}}</text>\n\t\t\t\t\t\t\t\t</view>\n\t\t\t\t\t\t\t</view>\n\t\t\t\t\t\t</block>\n\t\t\t\t\t</view>\n\t\t\t\t</swiper-item>\n\t\t\t</block>\n\t\t</swiper>\n\t</sticky-section>\n\t<sticky-section push-pinned-header=\"{{false}}\" padding=\"{{padding}}\">\n\t\t<sticky-header>\n\t\t\t<scroll-view\n\t\t\t class=\"vip-categorys-list\"\n\t\t\t scroll-x\n\t\t\t type=\"list\"\n\t\t\t show-scrollbar=\"{{false}}\"\n\t\t\t scroll-with-animation\n\t\t\t scroll-into-view=\"{{intoView}}\"\n\t\t\t scroll-into-view-alignment=\"center\"\n\t\t\t>\n\t\t\t\t<block wx:for=\"{{vipCategorys}}\" wx:key=\"id\">\n\t\t\t\t\t<view class=\"vip-category-item\" style=\"padding-left: {{index === 0 ? 20 : 40}}px;\" id=\"vip-category-{{index}}\">\n\t\t\t\t\t\t<text class=\"vip-category-name {{selected === index ? 'selected' : ''}}\" data-id=\"{{index}}\" bind:tap=\"chooseVipCategory\">\n\t\t\t\t\t\t\t{{item.name}}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t</view>\n\t\t\t\t</block>\n\t\t\t</scroll-view>\n\t\t</sticky-header>\n\t\t<grid-view cross-axis-gap=\"6\" cross-axis-count=\"2\" type=\"masonry\">\n\t\t\t<block wx:for=\"{{goods}}\" wx:key=\"id\">\n\t\t\t\t<view class=\"good\">\n\t\t\t\t\t<image\n\t\t\t\t\t mode=\"widthFix\"\n\t\t\t\t\t class=\"good-icon\"\n\t\t\t\t\t fade-in\n\t\t\t\t\t src=\"{{item.icon}}\"\n\t\t\t\t\t/>\n\t\t\t\t\t<view class=\"good-title\">\n\t\t\t\t\t\t<text>{{item.title}}</text>\n\t\t\t\t\t</view>\n\t\t\t\t\t<view class=\"good-comment\">\n\t\t\t\t\t\t<text>3万+评价</text>\n\t\t\t\t\t</view>\n\t\t\t\t</view>\n\t\t\t</block>\n\t\t</grid-view>\n\t</sticky-section>\n</scroll-view>\n\n"
  },
  {
    "path": "examples/refresher-two-level/project.config.json",
    "content": "{\n    \"appid\": \"wxe5f52902cf4de896\",\n    \"compileType\": \"miniprogram\",\n    \"libVersion\": \"3.1.5\",\n    \"packOptions\": {\n        \"ignore\": [],\n        \"include\": []\n    },\n    \"setting\": {\n        \"coverView\": true,\n        \"es6\": true,\n        \"postcss\": true,\n        \"minified\": true,\n        \"enhance\": true,\n        \"showShadowRootInWxmlPanel\": true,\n        \"packNpmRelationList\": [],\n        \"babelSetting\": {\n            \"ignore\": [],\n            \"disablePlugins\": [],\n            \"outputPath\": \"\"\n        },\n        \"useCompilerPlugins\": [\n            \"typescript\",\n            \"less\"\n        ]\n    },\n    \"condition\": {},\n    \"editorSetting\": {\n        \"tabIndent\": \"auto\",\n        \"tabSize\": 4\n    },\n    \"projectname\": \"refresher-two-level\"\n}"
  },
  {
    "path": "examples/refresher-two-level/util.js",
    "content": "export function getCategory() {\n\tlet categorys = [\n\t  '中小商户',\n\t  '商超零售',\n\t  '品牌服饰',\n\t  '餐饮',\n\t  '医疗',\n\t  '酒旅',\n\t  '政务',\n\t  '开发技术',\n\t  '产品能力',\n\t  '运营规范',\n\t]\n\tlet images = [\n\t  'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJgc9If4xjgvN3O4UQclWMiJxMoExkarf71FN-3SSf3Sh-GoatfvTbKcPE-grH-1L9g',\n\t  'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJrmxgZKLSYHHwqx6YfJyqPnSNeIHovelr_r6GLFpsiCuCuBgYKBc68vBi0dJYSMeZA',\n\t  'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJk4e_uP2-FWEQKo5Ijp5itIrlf-qIXozTGY6D595Ri2YIoLCUS7YseOda2JLTAEz7Q',\n\t  'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJj6M4Aqf4ov3F7tRlrb62now5owS_Q6vkhsWjnU_uWVbBR84dTHxG4tzAcjwAqOGZQ',\n\t  'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJrBCMpypwJ_xFgSnas6etb7Y6JuMRRMBJ6cSMmbmSkCkOjCSPDdC_eLEK1_FT-d-PQ',\n\t  'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJitcJIfp9P4VSWxi3126XeiyZ2BnnH0xg-oIXAUgHBgaHjBMwxzSjSkEkTMRqzlKZw',\n\t  'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJnw5zq4f_XW3swKAowexqAbziuojU5W9v4CJixA-NDJShkfS0ne3KWY_6SB56yqb3g',\n\t  'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJu8Q1L10fFiMMnZVYnLoP1GuT0q26CJLtjSRfJAjTvj6DBNuWrzzMD9UYZEb-pznKA',\n\t  'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChUzvBckq_FnOrUIIgSP2hJ9cYByUIwgzBEaDCcF5YiPNMmcMg0ewQBn_nMCt-q71vsg',\n\t  'https://res.wx.qq.com/op_res/Zmvv0fisUjaMjuqWLhWWkuzGktaXJEQt46EaKsCKeT06Z4tROseXN0joI7h2qwzqyx2FUy57cveZL-8iArI8_Q',\n\t]\n\tcategorys = categorys.map((name, id) => ({\n\t  id,\n\t  name,\n\t  icon: images[(id % categorys.length)] //`/images/boy/b${id}.png`\n\t}))\n\treturn categorys\n  }\n  \n  export function getGoods(num) {\n\tconst titles = [\n\t  '小程序性能优化课程基于实际开发场景，提升小程序性能表现，满足用户体验',\n\t  '解析常见小程序违规类型，帮助大家更好理解平台规则',\n\t  '快速了解微信小程序在医疗行业的应用',\n\t  '小程序直播的企业实践案例。',\n\t  '微信客服轻松配置，入门必修',\n\t  '想做互联网的生意，可以通过微信怎么经营呢？',\n\t  '了解小程序开发动态，听官方为你解读新能力',\n\t  '医保支付、互联网医院、线上问诊...小程序如何帮助传统医院数字化？',\n\t  '内含开店指引、店铺运营和平台规则，帮你快速掌握小商店经营秘诀',\n\t  '浅谈连锁零售的私域流量运营'\n\t]\n  \n\tconst images = [\n\t  'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU_LhYxhaP5JTy7TWgezsDY7RW_l_e04fR7oG7sCKmS8hc8mVeZaY6eUWT3nk-ww_ZQ',\n\t  'https://res.wx.qq.com/op_res/Zmvv0fisUjaMjuqWLhWWkuzGktaXJEQt46EaKsCKeT06Z4tROseXN0joI7h2qwzqyx2FUy57cveZL-8iArI8_Q',\n\t  'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJrBCMpypwJ_xFgSnas6etb7Y6JuMRRMBJ6cSMmbmSkCkOjCSPDdC_eLEK1_FT-d-PQ',\n\t  'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU-O3axOjUJGFgutF9Xc1JL1uxXFWYdW85mWG0Zvm5nv7rvP18CJ0q6-RRFM0xWLLog',\n\t  'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU3ywQmrV-rSREDwo0Hp9m7iIZZ7Njvjq_TlOg_0ss0cgQL0pfKOuB2NRpAcwfALxvw',\n\t  'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJgc9If4xjgvN3O4UQclWMiJxMoExkarf71FN-3SSf3Sh-GoatfvTbKcPE-grH-1L9g',\n\t  'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJu8Q1L10fFiMMnZVYnLoP1GuT0q26CJLtjSRfJAjTvj6DBNuWrzzMD9UYZEb-pznKA',\n\t  'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU1GROxmiPIBOCoA5Es44GxjN0KuCQQsoxEH33l05TCgk04n0dssHAIPxIV2ycSlSJA',\n\t  'https://res.wx.qq.com/op_res/RBwYn_b7VGuWuLJ2qBChU68wmBQzYcfQfuIAh1IKWq7OyG0EWxdWGhotYHFh-k-JpmkJ1Otq-mYUT8Dp3iucvg',\n\t  'https://res.wx.qq.com/op_res/mGK9l-4vYzVgHuIz_uFeJrmxgZKLSYHHwqx6YfJyqPnSNeIHovelr_r6GLFpsiCuCuBgYKBc68vBi0dJYSMeZA'\n\t]\n  \n\tconst goods = []\n\tfor (let id = 0; id < num; id++) {\n\t  goods.push({\n\t\tid,\n\t\ttitle: titles[(id % titles.length)],\n\t\ticon: images[(id % titles.length)] // `/images/goods/g${(id % num)}.jpg`\n\t  })\n\t}\n\treturn goods\n  }\n  \n  export function getVIPCategory() {\n\tlet vipCategorys = [\n\t  '本月最热',\n\t  '官方经营',\n\t  '行业实践',\n\t  '微信服务商'\n\t]\n\tvipCategorys = vipCategorys.map((name, id) => ({\n\t  id,\n\t  name\n\t}))\n\treturn vipCategorys\n  }\n  \n  export function getExpCategory() {\n\tlet expCategorys = [\n\t  '推荐',\n\t  '产品课程',\n\t  '技术课程',\n\t  '运营课程'\n\t]\n\texpCategorys = expCategorys.map((name, id) => ({\n\t  id,\n\t  name\n\t}))\n\treturn expCategorys\n  }\n  \n  export function getVideoList(num) {\n\tconst titles = [\n\t  '酷跑春天·跑鞋新品发布会',\n\t  '直接全球气候变化',\n\t  '幻想奇遇·3D音乐节'\n\t]\n\tconst urls = [\n\t  'http://mmbiz.qpic.cn/mmbiz_jpg/PxLPibq1ibyh3b3EQ9Ngrejypictlz1ZNMJVnBg3fkibIA3F1qajkQlVG2eKtbxltWEGrvYjfrLa5ib54wvnRMDLNDw/0?wx_fmt=jpeg',\n\t  'http://mmbiz.qpic.cn/mmbiz_jpg/PxLPibq1ibyh2zgPxemO7asicBcbcTHb2icticjEqxJKLtxhVOFVHjmCvto8YLjLpGP9p73ia78sicjvBDObNicocSIZ3A/0?wx_fmt=jpeg',\n\t  'http://mmbiz.qpic.cn/mmbiz_jpg/PxLPibq1ibyh37FxQAIO6GuOWdvfadyoic6flNp9cm9Czs9djzdyD6pOZcJ5Mfa2XUjZXlUoALaOtEAauGwmbibLnQ/0?wx_fmt=jpeg',\n\t  'http://mmbiz.qpic.cn/mmbiz_jpg/PxLPibq1ibyh0dEhZyMCWicMCVDXxNb44ZtTA6RT8c2FVt7FGPPO4r1UUCicQERZDXPiaVS5yPjN4HaSvvIwbxOp2rQ/0?wx_fmt=jpeg',\n\t  'http://mmbiz.qpic.cn/mmbiz_jpg/PxLPibq1ibyh3GeJcbCXCxqXNbRVSvb1757BmGxric35XZecmo9ctlU2dsMIL5PqCjUMUN0OTGcoMPUndO1euRcow/0?wx_fmt=jpeg'\n\t]\n\tconst videos = []\n\tfor (let id = 0; id < num; id++) {\n\t  videos.push({\n\t\tid,\n\t\ttitle: titles[(id % titles.length)],\n\t\turl: urls[(id % urls.length)]\n\t  })\n\t}\n\treturn videos\n  }"
  },
  {
    "path": "examples/segmented-half-screen/.eslintrc.js",
    "content": "/*\n * Eslint config file\n * Documentation: https://eslint.org/docs/user-guide/configuring/\n * Install the Eslint extension before using this feature.\n */\nmodule.exports = {\n  env: {\n    es6: true,\n    browser: true,\n    node: true,\n  },\n  ecmaFeatures: {\n    modules: true,\n  },\n  parserOptions: {\n    ecmaVersion: 2018,\n    sourceType: 'module',\n  },\n  globals: {\n    wx: true,\n    App: true,\n    Page: true,\n    getCurrentPages: true,\n    getApp: true,\n    Component: true,\n    requirePlugin: true,\n    requireMiniProgram: true,\n  },\n  // extends: 'eslint:recommended',\n  rules: {},\n}\n"
  },
  {
    "path": "examples/segmented-half-screen/app.js",
    "content": "// app.js\nApp({})\n"
  },
  {
    "path": "examples/segmented-half-screen/app.json",
    "content": "{\n  \"pages\": [\n    \"pages/index/index\"\n  ],\n  \"window\": {\n    \"backgroundTextStyle\": \"light\",\n    \"navigationBarBackgroundColor\": \"#fff\",\n    \"navigationBarTitleText\": \"Weixin\",\n    \"navigationBarTextStyle\": \"black\"\n  },\n  \"style\": \"v2\",\n  \"renderer\": \"skyline\",\n  \"rendererOptions\": {\n    \"skyline\": {\n      \"defaultDisplayBlock\": true,\n      \"disableABTest\": true,\n      \"sdkVersionBegin\": \"3.0.0\",\n      \"sdkVersionEnd\": \"15.255.255\"\n    }\n  },\n  \"lazyCodeLoading\": \"requiredComponents\",\n  \"componentFramework\": \"glass-easel\",\n  \"sitemapLocation\": \"sitemap.json\"\n}"
  },
  {
    "path": "examples/segmented-half-screen/app.wxss",
    "content": ""
  },
  {
    "path": "examples/segmented-half-screen/pages/index/comment-data.js",
    "content": "// 获取留言列表\nexport function getCommentList() {\n  return [\n    {\n      \"comment\": \"为了进一步优化小程序性能，提供更为接近原生的用户体验，我们在 WebView 渲染之外新增了一个渲染引擎 Skyline，其使用更精简高效的渲染管线，并带来诸多增强特性，让 Skyline 拥有更接近原生渲染的性能体验。\",\n      \"userName\": \"binnie\",\n      \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n      \"subCommentList\": [\n        {\n          \"comment\": \"界面更不容易被逻辑阻塞，进一步减少卡顿。\",\n          \"userName\": \"拖拉机🚜\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/0AG3_hOKnGAqBhAhBx_a__0nu3Q_hGgnBQgiQhJMqZrvroKqdtYXhcSUdlp59bXjx7qF-ddTwGCcB-AqzYmlrw\",\n          \"replyUserName\": \"binnie\"\n        },\n        {\n          \"comment\": \"无需为每个页面新建一个 JS 引擎实例（WebView），减少了内存、时间开销。\",\n          \"userName\": \"binnie\",\n          \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n          \"replyUserName\": \"拖拉机🚜\",\n        },\n        {\n          \"comment\": \"框架可以在页面之间共享更多的资源，进一步减少运行时内存、时间开销。\",\n          \"userName\": \"拖拉机🚜\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/0AG3_hOKnGAqBhAhBx_a__0nu3Q_hGgnBQgiQhJMqZrvroKqdtYXhcSUdlp59bXjx7qF-ddTwGCcB-AqzYmlrw\",\n          \"replyUserName\": \"binnie\"\n        },\n        {\n          \"comment\": \"框架的代码之间无需再通过 JSBridge 进行数据交换，减少了大量通信时间开销。\",\n          \"userName\": \"binnie\",\n          \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n          \"replyUserName\": \"拖拉机🚜\",\n        }\n      ]\n    },\n    {\n      \"comment\": \"Skyline 以性能为首要目标，因此特性上在满足基本需求的前提下进行了大幅精简，目前 Skyline 只实现了 CSS 特性的子集。在编码上，Skyline 与 WebView 模式保持一致，仍使用 WXML 和 WXSS 编写界面。在不采用 Skyline 新增特性的情况下，适配了 Skyline 的小程序在低版本或未支持 Skyline 的平台上可无缝自动退回到 WebView 渲染。\",\n      \"userName\": \"拖拉机🚜\",\n      \"userHeadImg\": \"https://res.wx.qq.com/op_res/0AG3_hOKnGAqBhAhBx_a__0nu3Q_hGgnBQgiQhJMqZrvroKqdtYXhcSUdlp59bXjx7qF-ddTwGCcB-AqzYmlrw\",\n      \"subCommentList\": [\n        {\n          \"comment\": \"基于 Worklet 机制的 动画模块，能够在渲染线程同步运行动画相关逻辑。\",\n          \"userName\": \"拖拉机🚜\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/0AG3_hOKnGAqBhAhBx_a__0nu3Q_hGgnBQgiQhJMqZrvroKqdtYXhcSUdlp59bXjx7qF-ddTwGCcB-AqzYmlrw\",\n          \"replyUserName\": \"binnie\"\n        },\n        {\n          \"comment\": \"基于 Worklet 机制的 手势系统。在渲染线程同步监听手势、执行手势相关逻辑；支持手势协商处理；\",\n          \"userName\": \"小苹果🍎\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJBAK7KJg9KId_6N1-rJ4OyCCQoJGVMOaTabboo2viRucoxkPvHRkn2fVl6tectlzBg\",\n          \"replyUserName\": \"binnie\"\n        },\n        {\n          \"comment\": \"基于 Worklet 机制的 自定义路由模块，支持实现自定义路由动画和交互。\",\n          \"userName\": \"流星雨\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYf3q0W302-kseD8VxLKoItZ6HgneLkgpQSEMIgEKz_xVE7putZxs2YEYqB13Uh37_w\",\n          \"replyUserName\": \"binnie\"\n        },\n        {\n          \"comment\": \"支持 跨页面共享元素，能够将上一个页面的元素“共享”到下一个页面，并伴随着过渡动画。\",\n          \"userName\": \"落日余晖\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYYjda9Dp372N3T05q_nn3PgvoXBoReXvaXBfkthtXQLN7m5_YI6FoTre-xvJBDFLMA\",\n          \"replyUserName\": \"binnie\"\n        },\n      ]\n    },\n    {\n      \"comment\": \"Skyline 能很好地保持和原有架构的兼容性，基于 WebView 环境的小程序代码基本上无需任何改动即可直接在新的架构下运行。\",\n      \"userName\": \"小苹果🍎\",\n      \"userHeadImg\": \"https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJBAK7KJg9KId_6N1-rJ4OyCCQoJGVMOaTabboo2viRucoxkPvHRkn2fVl6tectlzBg\",\n      \"subCommentList\": []\n    },\n    {\n      \"comment\": \"Skyline 支持了一些 Web 所缺失的但很重要的能力，以满足开发者实现更好的交互体验。\",\n      \"userName\": \"binnie\",\n      \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n      \"subCommentList\": [\n\n      ]\n    },\n    {\n      \"comment\": \"worklet 动画可以做到类原生动画般的体验。\",\n      \"userName\": \"流星雨\",\n      \"userHeadImg\": \"https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYf3q0W302-kseD8VxLKoItZ6HgneLkgpQSEMIgEKz_xVE7putZxs2YEYqB13Uh37_w\",\n      \"subCommentList\": [\n        {\n          \"comment\": \"提供如 timing、spring 等常见动画方式的封装方法，开发者可自定义动画曲线，同时可对不同的动画类型进行组合、重复，形成交织动画。😄\",\n          \"userName\": \"binnie\",\n          \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n          \"replyUserName\": \"流星雨\"\n        }\n      ]\n    },\n    {\n      \"comment\": \"Skyline 中 wxs 代码运行在 AppService 线程，而事件产生在 UI 线程，因此 wxs 动画 性能有所降低，为了提升小程序交互体验的效果，我们内置了一批手势组件。\",\n      \"userName\": \"落日余晖\",\n      \"userHeadImg\": \"https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYYjda9Dp372N3T05q_nn3PgvoXBoReXvaXBfkthtXQLN7m5_YI6FoTre-xvJBDFLMA\",\n      \"subCommentList\": [\n        {\n          \"comment\": \"免去开发者监听 touch 事件，自行计算手势逻辑的复杂步骤；\",\n          \"userName\": \"binnie\",\n          \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n          \"replyUserName\": \"落日余晖\"\n        },\n        {\n          \"comment\": \"手势组件直接在 UI 线程响应，避免了传递到 JS 线程带来的延迟；\",\n          \"userName\": \"落日余晖\",\n          \"userHeadImg\": \"https://res.wx.qq.com/op_res/7_miJnK0wxIrh5bV2QqvYYjda9Dp372N3T05q_nn3PgvoXBoReXvaXBfkthtXQLN7m5_YI6FoTre-xvJBDFLMA\",\n          \"replyUserName\": \"binnie\"\n        }\n      ]\n    },\n    {\n      \"comment\": \"在连续的 Skyline 页面间跳转时，可实现自定义路由效果，路由动画的曲线、时长均可交由开发者控制。\",\n      \"userName\": \"binnie\",\n      \"userHeadImg\": \"http://wx.qlogo.cn/mmhead/uI5pczeERTajXl904XSbHwAtGENC5ccKvo2F54sgYeqibHxOXNAFKdg/132\",\n      \"subCommentList\": []\n    },\n    {\n      \"comment\": \"在连续的两个 Skyline 页面跳转时，可以将上一个页面的元素“共享”到下一个页面，并伴随着过渡动画。\",\n      \"userName\": \"绿意盎然\",\n      \"userHeadImg\": \"https://res.wx.qq.com/op_res/0-l2fyKjv3_BR62E3KwTJH2f0R4uXyqnNGlrivO8cKbn0nz1DE_6s22rc91zluwIrqiAVZNREvCeVYAUS8aaZw\",\n      \"subCommentList\": []\n    },\n  ]\n}\n"
  },
  {
    "path": "examples/segmented-half-screen/pages/index/index.js",
    "content": "import { getCommentList } from \"./comment-data\"\n\nfunction clamp(val, min, max) {\n  'worklet'\n  return Math.min(Math.max(val, min), max)\n}\n\nconst { shared, timing } = wx.worklet\n\nconst GestureState = {\n  POSSIBLE: 0, // 0 此时手势未识别，如 panDown等\n  BEGIN: 1, // 1 手势已识别\n  ACTIVE: 2, // 2 连续手势活跃状态\n  END: 3, // 3 手势终止\n  CANCELLED: 4, // 4 手势取消，\n}\n\nconst { screenHeight, statusBarHeight, safeArea} = wx.getSystemInfoSync()\n\nComponent({\n  data: {\n    scale: 16,\n    list: getCommentList(),\n  },\n  lifetimes: {\n    created() {\n      this.transY = shared(1000)\n      this.scrollTop = shared(0)\n      this.startPan = shared(true)\n      this.initTransY = shared(0) // 留言半屏的初始位置\n      this.upward = shared(false)\n    },\n    attached() {\n      this.setData({\n        height: screenHeight - statusBarHeight,\n      })\n    },\n    ready() {\n      const query = this.createSelectorQuery()\n      // ready 生命周期里才能获取到首屏的布局信息\n      query.select('.comment-header').boundingClientRect()\n      query.exec((res) => {\n        this.transY.value = this.initTransY.value = screenHeight - res[0].height - (screenHeight - safeArea.bottom)\n      })\n      // 通过 transY 一个 SharedValue 控制半屏的位置\n      this.applyAnimatedStyle('.comment-container', () => {\n        'worklet'\n        return { transform: `translateY(${this.transY.value}px)` }\n      })\n    },\n  },\n  methods: {\n    setMapScale(scale) {\n      this.setData({ scale })\n    },\n    scrollTo(toValue) {\n      'worklet'\n      let scale = 18\n      if (toValue > screenHeight / 2) {\n        scale = 16\n      }\n      wx.worklet.runOnJS(this.setMapScale.bind(this))(scale)\n\n      this.transY.value = timing(toValue, { duration: 200 })\n    },\n    // shouldPanResponse 和 shouldScrollViewResponse 用于 pan 手势和 scroll-view 滚动手势的协商\n    shouldPanResponse() {\n      'worklet'\n      return this.startPan.value\n    },\n    shouldScrollViewResponse(pointerEvent) {\n      'worklet'\n      // transY > 0 说明 pan 手势在移动半屏，此时滚动不应生效\n      if (this.transY.value > statusBarHeight) return false\n\n      const scrollTop = this.scrollTop.value\n      const { deltaY } = pointerEvent\n      // deltaY > 0 是往上滚动，scrollTop <= 0 是滚动到顶部边界，此时 pan 开始生效，滚动不生效\n      const result = scrollTop <= 0 && deltaY > 0\n      this.startPan.value = result\n      return !result\n    },\n    // 处理拖动半屏的手势\n    handlePan(gestureEvent) {\n      'worklet'\n      // 滚动半屏的位置\n      if (gestureEvent.state === GestureState.ACTIVE) {\n        // deltaY < 0，往上滑动\n        this.upward.value = gestureEvent.deltaY < 0\n        // 当前半屏位置\n        const curPosition = this.transY.value\n        // 只能在 [statusBarHeight, screenHeight] 之间移动\n        const destination = clamp(curPosition + gestureEvent.deltaY, statusBarHeight, screenHeight)\n        if (curPosition === destination) return\n        // 改变 transY，来改变半屏的位置\n        this.transY.value = destination\n      }\n\n      if (gestureEvent.state === GestureState.END || gestureEvent.state === GestureState.CANCELLED) {\n        if (this.transY.value <= screenHeight / 2) {\n          // 在上面的位置\n          if (this.upward.value) {\n            this.scrollTo(statusBarHeight)\n          } else {\n            this.scrollTo(screenHeight / 2)\n          }\n        } else if (this.transY.value > screenHeight / 2 && this.transY.value <= this.initTransY.value) {\n          // 在中间位置的时候\n          if (this.upward.value) {\n            this.scrollTo(screenHeight / 2)\n          } else {\n            this.scrollTo(this.initTransY.value)\n          }\n        } else {\n          // 在最下面的位置\n          this.scrollTo(this.initTransY.value)\n        }\n      }\n    },\n    adjustDecelerationVelocity(velocity) {\n      'worklet'\n      const scrollTop = this.scrollTop.value\n      return scrollTop <= 0 ? 0 : velocity\n    },\n    handleScroll(evt) {\n      'worklet'\n      this.scrollTop.value = evt.detail.scrollTop\n    },\n    // 简单兼容 WebView\n    handleTouchEnd() {\n      if (this.renderer === 'skyline') {\n        return\n      }\n      if (this.transY.value === statusBarHeight) {\n        this.lastTransY = statusBarHeight\n        this.scrollTo(screenHeight / 2)\n      } else if (this.transY.value === screenHeight / 2 && this.lastTransY === statusBarHeight) {\n        this.lastTransY = screenHeight / 2\n        this.scrollTo(this.initTransY.value)\n      } else if (this.transY.value === this.initTransY.value) {\n        this.lastTransY = this.initTransY.value\n        this.scrollTo(screenHeight / 2)\n      } else if (this.transY.value === screenHeight / 2 && this.lastTransY === this.initTransY.value) {\n        this.scrollTo(statusBarHeight)\n      }\n    },\n  },\n})\n"
  },
  {
    "path": "examples/segmented-half-screen/pages/index/index.json",
    "content": "{\n  \"usingComponents\": {},\n  \"disableScroll\": true,\n  \"navigationStyle\": \"custom\"\n}"
  },
  {
    "path": "examples/segmented-half-screen/pages/index/index.wxml",
    "content": "<map id=\"myMap\" scale=\"{{scale}}\" latitude=\"23.099994\" longitude=\"113.324520\"></map>\n\n<view class=\"comment-container\" style=\"height: {{height}}px;\">\n  <!-- 顶部不参与手势协商，单独控制 -->\n  <pan-gesture-handler worklet:ongesture=\"handlePan\" style=\"flex-shrink: 0;\">\n    <view class=\"comment-header\" bind:touchend=\"handleTouchEnd\">\n      <view class=\"comment-handler\"></view>\n      留言\n    </view>\n  </pan-gesture-handler>\n  <!-- 滚动区要与 pan 手势协商 -->\n  <pan-gesture-handler id=\"pan\" worklet:should-response-on-move=\"shouldPanResponse\" simultaneousHandlers=\"{{['scroll']}}\" worklet:ongesture=\"handlePan\">\n    <vertical-drag-gesture-handler id=\"scroll\" native-view=\"scroll-view\" worklet:should-response-on-move=\"shouldScrollViewResponse\" simultaneousHandlers=\"{{['pan']}}\">\n      <scroll-view class=\"comment-list\" scroll-y worklet:adjust-deceleration-velocity=\"adjustDecelerationVelocity\" worklet:onscrollupdate=\"handleScroll\" type=\"list\" show-scrollbar=\"{{false}}\">\n        <view class=\"comment-item\" wx:for=\"{{list}}\" wx:key=\"comment\">\n          <view class=\"main-comment\">\n            <image fade-in class=\"user-head-img\" src=\"{{item.userHeadImg}}\"></image>\n            <view class=\"others\">\n              <text class=\"user-name\">{{item.userName}}</text>\n              <text class=\"content\">{{item.comment}}</text>\n            </view>\n          </view>\n          <view class=\"sub-comment\" wx:for=\"{{item.subCommentList}}\" wx:key=\"comment\" wx:for-item=\"subItem\" wx:for-index=\"subIndex\">\n            <image fade-in class=\"user-head-img\" src=\"{{subItem.userHeadImg}}\"></image>\n            <view class=\"others\">\n              <text class=\"user-name\">{{subItem.userName}} 回复 {{subItem.replyUserName}}</text>\n              <text class=\"content\">{{subItem.comment}}</text>\n            </view>\n          </view>\n        </view>\n        <view class=\"safe-area-inset-bottom\"></view>\n      </scroll-view>\n    </vertical-drag-gesture-handler>\n  </pan-gesture-handler>\n</view>\n"
  },
  {
    "path": "examples/segmented-half-screen/pages/index/index.wxss",
    "content": "page {\n  display: flex;\n  flex-direction: column;\n  width: 100vw;\n  height: 100vh;\n  color: #1A191E;\n}\npage, view {\n  box-sizing: border-box;\n}\npan-gesture-handler, vertical-drag-gesture-handler {\n  display: flex;\n  flex-direction: column;\n  overflow: hidden;\n}\n.container {\n  flex: 1;\n  width: 100vw;\n  overflow: hidden;\n}\n.container image {\n  width: 100vw;\n}\n\n#myMap {\n  width: 100vw;\n  height: 100vh;\n}\n\n.open-comment {\n  display: flex;\n  flex-direction: column;\n  flex-shrink: 0;\n  width: 100%;\n  background-color: white;\n}\n.open-comment-wording {\n  height: 66px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n.safe-area-inset-bottom {\n  height: env(safe-area-inset-bottom);\n}\n\n.comment-container {\n  width: 100vw;\n  height: 100vh;\n  display: flex;\n  flex-direction: column;\n  position: absolute;\n  top: 0;\n  z-index: 999;\n  background-color: white;\n  border-top-left-radius: 20px;\n  border-top-right-radius: 20px;\n  box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.2);\n}\n.comment-header {\n  width: 100%;\n  font-size: 16px;\n  text-align: center;\n  padding: 15px 0;\n}\n.comment-handler {\n  width: 50px;\n  height: 3px;\n  border-radius: 3px;\n  background-color: #EEE;\n  margin: 0 auto 10px;\n}\n.comment-list {\n  flex: 1;\n  overflow: hidden;\n}\n.comment-item {\n  padding: 0 20px 20px;\n  font-size: 13px;\n  line-height: 1.4;\n}\n.main-comment, .sub-comment {\n  display: flex;\n  flex-direction: row;\n}\n.sub-comment {\n  padding: 10px 22px 0;\n}\n\n.user-head-img {\n  width: 33px;\n  height: 33px;\n  border-radius: 50%;\n  margin-top: 5px;\n}\n.others {\n  flex: 1;\n  margin-left: 10px;\n}\n.user-name {\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n.content {\n  margin-top: 2px;\n}\n"
  },
  {
    "path": "examples/segmented-half-screen/project.config.json",
    "content": "{\n  \"appid\": \"wxe5f52902cf4de896\",\n  \"compileType\": \"miniprogram\",\n  \"libVersion\": \"latest\",\n  \"packOptions\": {\n    \"ignore\": [],\n    \"include\": []\n  },\n  \"setting\": {\n    \"coverView\": true,\n    \"es6\": true,\n    \"postcss\": true,\n    \"minified\": true,\n    \"enhance\": true,\n    \"showShadowRootInWxmlPanel\": true,\n    \"packNpmRelationList\": [],\n    \"babelSetting\": {\n      \"ignore\": [],\n      \"disablePlugins\": [],\n      \"outputPath\": \"\"\n    },\n    \"condition\": false,\n    \"skylineRenderEnable\": false,\n    \"compileWorklet\": true\n  },\n  \"condition\": {},\n  \"editorSetting\": {\n    \"tabIndent\": \"insertSpaces\",\n    \"tabSize\": 2\n  }\n}"
  },
  {
    "path": "examples/segmented-half-screen/project.private.config.json",
    "content": "{\n  \"description\": \"项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档：https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html\",\n  \"projectname\": \"segmented\",\n  \"setting\": {\n    \"compileHotReLoad\": false,\n    \"skylineRenderEnable\": true\n  },\n  \"libVersion\": \"latest\"\n}"
  },
  {
    "path": "examples/segmented-half-screen/sitemap.json",
    "content": "{\n    \"desc\": \"关于本文件的更多信息，请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html\",\n    \"rules\": [{\n    \"action\": \"allow\",\n    \"page\": \"*\"\n    }]\n}"
  },
  {
    "path": "examples/tab-indicator/.eslintrc.js",
    "content": "/*\n * Eslint config file\n * Documentation: https://eslint.org/docs/user-guide/configuring/\n * Install the Eslint extension before using this feature.\n */\nmodule.exports = {\n  env: {\n    es6: true,\n    browser: true,\n    node: true,\n  },\n  ecmaFeatures: {\n    modules: true,\n  },\n  parserOptions: {\n    ecmaVersion: 2018,\n    sourceType: 'module',\n  },\n  globals: {\n    wx: true,\n    App: true,\n    Page: true,\n    getCurrentPages: true,\n    getApp: true,\n    Component: true,\n    requirePlugin: true,\n    requireMiniProgram: true,\n  },\n  // extends: 'eslint:recommended',\n  rules: {},\n}\n"
  },
  {
    "path": "examples/tab-indicator/app.js",
    "content": "// app.js\nApp({})\n"
  },
  {
    "path": "examples/tab-indicator/app.json",
    "content": "{\n  \"pages\": [\n      \"pages/index/index\"\n  ],\n  \"window\": {\n    \"backgroundTextStyle\": \"light\",\n    \"navigationBarBackgroundColor\": \"#fff\",\n    \"navigationBarTitleText\": \"Weixin\",\n    \"navigationBarTextStyle\": \"black\"\n  },\n  \"style\": \"v2\",\n  \"renderer\": \"skyline\",\n  \"rendererOptions\": {\n    \"skyline\": {\n      \"defaultDisplayBlock\": true,\n      \"disableABTest\": true,\n      \"sdkVersionBegin\": \"3.0.0\",\n      \"sdkVersionEnd\": \"15.255.255\"\n    }\n  },\n  \"lazyCodeLoading\": \"requiredComponents\",\n  \"componentFramework\": \"glass-easel\",\n  \"sitemapLocation\": \"sitemap.json\"\n}"
  },
  {
    "path": "examples/tab-indicator/app.wxss",
    "content": "/**app.wxss**/\n.container {\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: space-between;\n  padding: 200rpx 0;\n  box-sizing: border-box;\n} \n"
  },
  {
    "path": "examples/tab-indicator/components/navigation-bar/index.js",
    "content": "Component({\n  options: {\n    multipleSlots: true // 在组件定义时的选项中启用多slot支持\n  },\n  /**\n   * 组件的属性列表\n   */\n  properties: {\n    extClass: {\n      type: String,\n      value: ''\n    },\n    title: {\n      type: String,\n      value: ''\n    },\n    background: {\n      type: String,\n      value: ''\n    },\n    color: {\n      type: String,\n      value: ''\n    },\n    back: {\n      type: Boolean,\n      value: true\n    },\n    loading: {\n      type: Boolean,\n      value: false\n    },\n    animated: {\n      // 显示隐藏的时候opacity动画效果\n      type: Boolean,\n      value: true\n    },\n    show: {\n      // 显示隐藏导航，隐藏的时候navigation-bar的高度占位还在\n      type: Boolean,\n      value: true,\n      observer: '_showChange'\n    },\n    // back为true的时候，返回的页面深度\n    delta: {\n      type: Number,\n      value: 1\n    }\n  },\n  /**\n   * 组件的初始数据\n   */\n  data: {\n    displayStyle: ''\n  },\n  attached() {\n    const rect = wx.getMenuButtonBoundingClientRect()\n    wx.getSystemInfo({\n      success: (res) => {\n        this.setData({\n          statusBarHeight: res.statusBarHeight,\n          innerPaddingRight: `padding-right:${res.windowWidth - rect.left}px`,\n          leftWidth: `width:${res.windowWidth - rect.left}px`,\n          navBarHeight: rect.bottom + rect.top - res.statusBarHeight,\n        })\n      }\n    })\n  },\n  /**\n   * 组件的方法列表\n   */\n  methods: {\n    _showChange(show) {\n      const animated = this.data.animated\n      let displayStyle = ''\n      if (animated) {\n        displayStyle = `opacity: ${show ? '1' : '0'};transition: opacity 0.5s;`\n      } else {\n        displayStyle = `display: ${show ? '' : 'none'}`\n      }\n      this.setData({\n        displayStyle\n      })\n    },\n    back() {\n      const data = this.data\n      if (data.delta) {\n        wx.navigateBack({\n          delta: data.delta\n        })\n      }\n      this.triggerEvent('back', { delta: data.delta }, {})\n    }\n  }\n})\n"
  },
  {
    "path": "examples/tab-indicator/components/navigation-bar/index.json",
    "content": "{\n  \"component\": true,\n  \"usingComponents\": {},\n  \"componentFramework\": \"glass-easel\",\n  \"addGlobalClass\": true\n}"
  },
  {
    "path": "examples/tab-indicator/components/navigation-bar/index.wxml",
    "content": "<view class=\"weui-navigation-bar {{extClass}}\">\n  <view class=\"weui-navigation-bar__inner\" style=\"padding-top: {{statusBarHeight}}px; height: {{navBarHeight}}px; color: {{color}}; background: {{background}}; {{displayStyle}}; {{innerPaddingRight}};\">\n    <view class='weui-navigation-bar__left' style=\"{{leftWidth}}\">\n      <block wx:if=\"{{back}}\">\n        <view class=\"weui-navigation-bar__buttons\">\n          <view bindtap=\"back\" class=\"weui-navigation-bar__btn_goback_wrapper\" hover-class=\"weui-active\" aria-role=\"button\" aria-label=\"返回\">\n            <view class=\"weui-navigation-bar__button weui-navigation-bar__btn_goback\"></view>\n          </view>\n        </view>\n      </block>\n      <block wx:else>\n        <slot name=\"left\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__center'>\n      <view wx:if=\"{{loading}}\" class=\"weui-navigation-bar__loading\" aria-role=\"alert\">\n        <view class=\"weui-loading\" style=\"width:{{size.width}}rpx;height:{{size.height}}rpx;\" aria-role=\"img\" aria-label=\"加载中\"></view>\n      </view>\n      <block wx:if=\"{{title}}\">\n        <text>{{title}}</text>\n      </block>\n      <block wx:else>\n        <slot name=\"center\"></slot>\n      </block>\n    </view>\n\n    <view class='weui-navigation-bar__right'>\n      <slot name=\"right\"></slot>\n    </view>\n  </view>\n</view>\n"
  },
  {
    "path": "examples/tab-indicator/components/navigation-bar/index.wxss",
    "content": ".weui-navigation-bar {\n  overflow: hidden;\n  color: rgba(0, 0, 0, .9);\n  width: 100vw;\n}\n\n.weui-navigation-bar__placeholder {\n  background: #f7f7f7;\n  position: relative;\n}\n\n.weui-navigation-bar__inner, .weui-navigation-bar__inner .weui-navigation-bar__left {\n  display: flex;\n  align-items: center;\n  flex-direction: row;\n}\n\n.weui-navigation-bar__inner {\n  position: relative;\n  padding-right: 95px;\n  width: 100vw;\n  box-sizing: border-box;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left {\n  position: relative;\n  width: 95px;\n  padding-left: 16px;\n  box-sizing: border-box;\n}\n\n.weui-navigation-bar__btn_goback_wrapper {\n  padding: 11px 18px 11px 16px;\n  margin: -11px -18px -11px -16px;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__left .weui-navigation-bar__btn_goback {\n  font-size: 12px;\n  width: 12px;\n  height: 24px;\n  background: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E\") no-repeat 50% 50%;\n  background-size: cover;\n}\n\n.weui-navigation-bar__inner .weui-navigation-bar__center {\n  font-size: 17px;\n  text-align: center;\n  position: relative;\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-weight: bold;\n}\n\n@media(prefers-color-scheme: dark) {\n  .weui-navigation-bar {\n    color: hsla(0, 0%, 100%, .8);\n  }\n  .weui-navigation-bar__inner {\n    background-color: #1f1f1f;\n  }\n}\n"
  },
  {
    "path": "examples/tab-indicator/pages/index/index.js",
    "content": "const ScrollState = {\n  scrollStart: 0,\n  scrollUpdate: 1,\n  scrollEnd: 2,\n}\n\nconst tabs = [\n  {\n    title: '性能优化',\n    title2: '小程序性能优化实践',\n    img: 'http://mmbiz.qpic.cn/mmbiz_jpg/PxLPibq1ibyh0U4e0qLqNrULAUzW5UbWbicUN5GyJqd24GR0Ricg5q14VGGBWlicNca8x4xelvDrM1r0ibwAjAsR0bOA/0?wx_fmt=jpeg',\n    desc: '小程序性能优化课程基于实际开发场景，由资深开发者分享小程序性能优化的各项能力及应用实践，提升小程序性能表现，满足用户体验。',\n  },\n  {\n    title: '新能力解读',\n    title2: '小程序开发新能力解读',\n    img: 'http://mmbiz.qpic.cn/sz_mmbiz_png/GEWVeJPFkSH05EZMIBafqzpoZVSXtCE47V7icf0gru4KPUzMjIcIibJPUlXqbZib4VNmTecxef987XEWib2vhwuqbQ/0?wx_fmt=png',\n    desc: '这个月小程序释放了什么新能力？又有哪些新规则？收藏课程，及时了解小程序开发动态，听官方为你解读新能力。',\n  },\n  {\n    title: '基础开发',\n    title2: '小程序基础开发之架构、框架、组件',\n    img: 'http://mmbiz.qpic.cn/sz_mmbiz_jpg/GEWVeJPFkSEz7tgvlaTtv2MYO01RZr0yNgtbEZJzcbRl0deOWmSbX0UfRHPt78UCOxPIVYnhAiaJVib40SviaV1Vw/0?wx_fmt=jpeg',\n    desc: '小程序基础开发之架构、框架、组件，更多课程正在制作中...',\n  },\n]\n\nComponent({\n  data: {\n    selectedTab: 0,\n    tabs,\n    translateX: 0,\n  },\n\n  lifetimes: {\n    created() {\n      const shared = wx.worklet.shared\n      const { windowWidth } = wx.getSystemInfoSync()\n      const innerWindowWidth = windowWidth - 48 // 左右 padding 各 24\n      this._tabWidth = shared(innerWindowWidth / 3) // 通过 boundingClientRect 算也行\n      this._translateX = shared(0)\n      this._lastTranslateX = shared(0)\n      this._scaleX = shared(0.7)\n      this._windowWidth = shared(windowWidth)\n\n    },\n    attached() {\n      this.applyAnimatedStyle('.tab-border', () => {\n        'worklet'\n        return {\n          transform: `translateX(${this._translateX.value}px) scaleX(${this._scaleX.value})`,\n        }\n      })\n    },\n  },\n\n  methods: {\n    onTapTab(evt) {\n      const { tab } = evt.currentTarget.dataset || {}\n      this.setData({\n        selectedTab: tab,\n      })\n    },\n\n    onTabChanged(evt) {\n      const index = evt.detail.current\n      this.setData({\n        selectedTab: index,\n      })\n      if (this.renderer !== 'skyline') {\n        this.setData({\n          translateX: this._tabWidth.value * index\n        })\n      }\n    },\n\n    // swiper 切换过程中每帧回调，声明为 worklet 函数使其跑在 UI 线程\n    onTabTransition(evt) {\n      'worklet'\n      // 这里 swiper item 是占满了整个屏幕宽度，算出拖动比例，换算成相对 tab width 的偏移\n      const translateRatio = evt.detail.dx / this._windowWidth.value\n      this._translateX.value = this._lastTranslateX.value + translateRatio * this._tabWidth.value\n\n      // 控制 scale 值，拖到中间时 scale 处于最大值 1，两端递减\n      const scaleRatio = (this._translateX.value / this._tabWidth.value) % 1\n      const changedScale = scaleRatio <= 0.5 ? scaleRatio : (1 - scaleRatio) // 最大值 0.5\n      this._scaleX.value = Math.abs(changedScale) * 0.6 + 0.7\n\n      if (evt.detail.state === ScrollState.scrollEnd) {\n        this._lastTranslateX.value = this._translateX.value\n      }\n    },\n\n    onTabTransitionEnd(evt) {\n      'worklet'\n      this.onTabTransition(evt)\n\n      // end\n      this._lastTranslateX.value = this._translateX.value\n    }\n  },\n})\n"
  },
  {
    "path": "examples/tab-indicator/pages/index/index.json",
    "content": "{\n  \"usingComponents\": {\n    \"navigation-bar\": \"../../components/navigation-bar\"\n  },\n  \"disableScroll\": true,\n  \"navigationStyle\": \"custom\"\n}\n"
  },
  {
    "path": "examples/tab-indicator/pages/index/index.wxml",
    "content": "<navigation-bar title=\"Tab 指示条\" back></navigation-bar>\n<view class=\"tab-container\">\n  <view class=\"tab-list\">\n    <view wx:for=\"{{tabs}}\" wx:key=\"title\" class=\"tab-item {{selectedTab === index ? 'active' : ''}}\" data-tab=\"{{index}}\" bindtap=\"onTapTab\">\n      <view>{{ item.title }}</view>\n    </view>\n    <view class=\"tab-border\" style=\"transform: translateX({{translateX}}px) scaleX(0.7);\"></view>\n  </view>\n  <swiper class=\"scroll-list\" current=\"{{selectedTab}}\" bind:change=\"onTabChanged\" worklet:onscrollstart=\"onTabTransition\" worklet:onscrollupdate=\"onTabTransition\" worklet:onscrollend=\"onTabTransitionEnd\" duration=\"{{400}}\" cache-extent=\"1\">\n    <swiper-item wx:for=\"{{tabs}}\" wx:key=\"title\">\n      <image class=\"item-image\" src=\"{{item.img}}\" mode=\"widthFix\"></image>\n      <view class=\"item-title\">{{item.title2}}</view>\n      <view>{{item.desc}}</view>\n    </swiper-item>\n  </swiper>\n</view>\n"
  },
  {
    "path": "examples/tab-indicator/pages/index/index.wxss",
    "content": "view, swiper-item {\n  box-sizing: border-box;\n}\n\n.tab-container {\n  display: flex;\n  flex-direction: column;\n  width: 100vw;\n  height: 100vh;\n}\n\n.tab-list {\n  display: flex;\n  flex-direction: row;\n  margin: 5px 24px 0;\n  position: relative;\n}\n\n.tab-item {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  justify-content: center;\n  position: relative;\n  flex: 1;\n  height: 50px;\n  border-bottom: 0.5px solid white;\n}\n\n.tab-item.active {\n  color: #07c160;\n}\n\n.tab-border {\n  position: absolute;\n  left: 0;\n  bottom: 0;\n  height: 3px;\n  background-color:  #07c160;\n  width: 33.33%;\n  transform: translateX(0) scaleX(0.7);\n  z-index: 1;\n}\n\n.scroll-list {\n  flex: 1;\n  width: 100%;\n  overflow: hidden;\n}\n\nswiper-item {\n  padding: 30px;\n}\n\n.item-image {\n  display: flex;\n  flex-direction: row;\n  justify-content: center;\n  align-items: center;\n  width: 100%;\n}\n\n.item-title {\n  padding: 20px 0 8px 0;\n  font-size: 18px;\n}\n"
  },
  {
    "path": "examples/tab-indicator/project.config.json",
    "content": "{\n  \"appid\": \"wxe5f52902cf4de896\",\n  \"compileType\": \"miniprogram\",\n  \"libVersion\": \"latest\",\n  \"packOptions\": {\n    \"ignore\": [],\n    \"include\": []\n  },\n  \"setting\": {\n    \"coverView\": true,\n    \"es6\": true,\n    \"postcss\": true,\n    \"minified\": true,\n    \"enhance\": true,\n    \"showShadowRootInWxmlPanel\": true,\n    \"packNpmRelationList\": [],\n    \"babelSetting\": {\n      \"ignore\": [],\n      \"disablePlugins\": [],\n      \"outputPath\": \"\"\n    },\n    \"condition\": false,\n    \"skylineRenderEnable\": true,\n    \"compileWorklet\": true\n  },\n  \"condition\": {},\n  \"editorSetting\": {\n    \"tabIndent\": \"insertSpaces\",\n    \"tabSize\": 2\n  }\n}"
  },
  {
    "path": "examples/tab-indicator/project.private.config.json",
    "content": "{\n  \"description\": \"项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档：https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html\",\n  \"projectname\": \"tab-indicator\",\n  \"setting\": {\n    \"compileHotReLoad\": false\n  },\n  \"libVersion\": \"latest\"\n}"
  },
  {
    "path": "examples/tab-indicator/sitemap.json",
    "content": "{\n    \"desc\": \"关于本文件的更多信息，请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html\",\n    \"rules\": [{\n    \"action\": \"allow\",\n    \"page\": \"*\"\n    }]\n}"
  }
]