You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2021/08/27 11:41:12 UTC

[echarts-handbook] branch master updated: add lazyload

This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/echarts-handbook.git


The following commit(s) were added to refs/heads/master by this push:
     new 00e9485  add lazyload
00e9485 is described below

commit 00e94854140539a2648d264b035ca12b3b7917e9
Author: pissang <bm...@gmail.com>
AuthorDate: Fri Aug 27 19:41:02 2021 +0800

    add lazyload
---
 components/helper/sandbox.ts                  | 117 ++++++++++--
 components/markdown/Example.vue               |  17 +-
 components/markdown/Live.vue                  |  29 ++-
 components/markdown/global.ts                 |   4 +
 contents/en/basics/release-note/v5-feature.md |  30 +--
 contents/en/meta/edit-guide.md                |   2 +-
 contents/zh/basics/release-note/v5-feature.md |  26 +--
 contents/zh/concepts/data-transform.md        |   2 +-
 contents/zh/meta/edit-guide.md                |   2 +-
 package-lock.json                             | 263 ++------------------------
 package.json                                  |   3 +
 pages/_.vue                                   |  38 +++-
 12 files changed, 223 insertions(+), 310 deletions(-)

diff --git a/components/helper/sandbox.ts b/components/helper/sandbox.ts
index 2223bf9..875ec41 100644
--- a/components/helper/sandbox.ts
+++ b/components/helper/sandbox.ts
@@ -1,32 +1,91 @@
+import Vue from 'vue'
 declare const echarts: any
 export function createSandbox() {
   let appEnv: any = {}
 
-  let _intervalIdList: number[] = []
-  let _timeoutIdList: number[] = []
+  const animatorIdMap = {}
+  let animatorId = 1
+  // Using echarts timer so it can be paused.
+  function chartSetTimeout(cb: () => void, time: number) {
+    const animator = chartInstance
+      .getZr()
+      .animation.animate({ val: 0 } as any, {
+        loop: false
+      })
+      .when(time, {
+        val: 1
+      })
+      .during(() => {
+        // Please don't fall sleep.
+        // TODO Can be configurable.
+        chartInstance.getZr().wakeUp()
+      })
+      .done(() => {
+        // NOTE: Must delay the callback. Or zrender flush will invoke the chartSetTimeout callback again.
+        // TODO: This is something needs to be fixed in zrender.
+        Vue.nextTick(cb)
+      })
+      .start()
 
-  const _oldSetTimeout = window.setTimeout
-  const _oldSetInterval = window.setInterval
+    return animator
+  }
+
+  function chartSetInterval(cb: () => void, time: number) {
+    const animator = chartInstance
+      .getZr()
+      .animation.animate({ val: 0 } as any, {
+        loop: true
+      })
+      .when(time, {
+        val: 1
+      })
+      .during((target, percent) => {
+        // Please don't fall sleep.
+        // TODO Can be configurable.
+        chartInstance.getZr().wakeUp()
+        if (percent === 1) {
+          // NOTE: Must delay the callback. Or zrender flush will invoke the chartSetTimeout callback again.
+          // TODO: This is something needs to be fixed in zrender.
+          Vue.nextTick(cb)
+        }
+      })
+      .start()
+
+    return animator
+  }
 
   function setTimeout(func, delay) {
-    var id = _oldSetTimeout(func, delay)
-    _timeoutIdList.push(id)
-    return id
+    const animator = chartSetTimeout(func, delay)
+    animatorIdMap[animatorId] = animator
+    return animatorId++
   }
   function setInterval(func, gap) {
-    var id = _oldSetInterval(func, gap)
-    _intervalIdList.push(id)
-    return id
+    const animator = chartSetInterval(func, gap)
+    animatorIdMap[animatorId] = animator
+    return animatorId++
   }
-  function _clearTimeTickers() {
-    for (var i = 0; i < _intervalIdList.length; i++) {
-      clearInterval(_intervalIdList[i])
+
+  function clearTimer(id) {
+    const animator = animatorIdMap[id]
+    if (animator) {
+      chartInstance.getZr().animation.removeAnimator(animator)
+      delete animatorIdMap[id]
     }
-    for (var i = 0; i < _timeoutIdList.length; i++) {
-      clearTimeout(_timeoutIdList[i])
+  }
+
+  function clearTimeout(id) {
+    clearTimer(id)
+  }
+  function clearInterval(id) {
+    clearTimer(id)
+  }
+
+  function _clearTimeTickers() {
+    for (let key in animatorIdMap) {
+      if (animatorIdMap.hasOwnProperty(key)) {
+        clearTimer(key)
+      }
     }
-    _intervalIdList = []
-    _timeoutIdList = []
   }
   const _events: string[] = []
   function _wrapOnMethods(chart) {
@@ -84,6 +143,18 @@ export function createSandbox() {
       return chartInstance
     },
 
+    pause() {
+      if (chartInstance) {
+        chartInstance.getZr().animation.pause()
+      }
+    },
+
+    resume() {
+      if (chartInstance) {
+        chartInstance.getZr().animation.resume()
+      }
+    },
+
     run(
       el: HTMLElement,
       code: string,
@@ -122,10 +193,18 @@ export function createSandbox() {
         'app',
         'setTimeout',
         'setInterval',
-        'ROOT_PATH',
+        'clearTimeout',
+        'clearInterval',
         'var option;\n' + compiledCode + '\nreturn option;'
       )
-      const option = func(chartInstance, appEnv, setTimeout, setInterval)
+      const option = func(
+        chartInstance,
+        appEnv,
+        setTimeout,
+        setInterval,
+        clearTimer,
+        clearInterval
+      )
       let updateTime = 0
 
       if (option && typeof option === 'object') {
diff --git a/components/markdown/Example.vue b/components/markdown/Example.vue
index a85ca98..c1da380 100644
--- a/components/markdown/Example.vue
+++ b/components/markdown/Example.vue
@@ -1,9 +1,14 @@
 <template>
-  <iframe :width="width" :height="height" :src="fullSrc"></iframe>
+  <iframe
+    :width="width"
+    :height="height"
+    :src="finalSrc"
+    v-observe-visibility="visibilityChanged"
+  ></iframe>
 </template>
 
 <script lang="ts">
-import { computed, defineComponent } from '@vue/composition-api'
+import { computed, defineComponent, ref } from '@vue/composition-api'
 import config from '~/configs/config'
 
 export default defineComponent({
@@ -28,8 +33,14 @@ export default defineComponent({
         ) + props.src
       )
     })
+    const finalSrc = ref('')
     return {
-      fullSrc
+      finalSrc,
+      visibilityChanged(isVisible) {
+        if (isVisible) {
+          finalSrc.value = fullSrc.value
+        }
+      }
     }
   }
 })
diff --git a/components/markdown/Live.vue b/components/markdown/Live.vue
index 376e818..372eb3e 100644
--- a/components/markdown/Live.vue
+++ b/components/markdown/Live.vue
@@ -1,5 +1,9 @@
 <template>
-  <div :class="`md-live layout-${layout}`" v-if="innerCode">
+  <div
+    :class="`md-live layout-${layout}`"
+    v-observe-visibility="visibilityChanged"
+    v-if="innerCode"
+  >
     <div class="md-live-editor">
       <div class="md-live-editor-container">
         <prism-editor v-model="innerCode" :highlight="highlighter">
@@ -110,9 +114,9 @@ export default defineComponent({
       debouncedUpdate()
     })
 
-    onMounted(() => {
-      debouncedUpdate()
-    })
+    // onMounted(() => {
+    //   debouncedUpdate()
+    // })
 
     onUnmounted(() => {
       removeListener(unref(previewContainer)!, resize)
@@ -123,6 +127,19 @@ export default defineComponent({
       previewContainer,
       highlighter(code) {
         return highlight(code, languages[props.lang] || languages.js)
+      },
+      visibilityChanged(isVisible) {
+        if (isVisible) {
+          if (!sandbox) {
+            debouncedUpdate()
+          } else {
+            sandbox.resume()
+          }
+        } else {
+          if (sandbox) {
+            sandbox.pause()
+          }
+        }
       }
     }
   }
@@ -183,6 +200,10 @@ export default defineComponent({
 
     font-size: 13px;
     padding: 10px;
+
+    @media (max-width: 768px) {
+      max-height: 300px;
+    }
   }
 
   pre {
diff --git a/components/markdown/global.ts b/components/markdown/global.ts
index f2eb9f5..7d6b8a7 100644
--- a/components/markdown/global.ts
+++ b/components/markdown/global.ts
@@ -1,5 +1,7 @@
 import Vue from 'vue'
 import VueCompositionAPI from '@vue/composition-api'
+import 'intersection-observer'
+import { ObserveVisibility } from 'vue-observe-visibility'
 
 import Example from './Example.vue'
 import Alert from './Alert.vue'
@@ -14,3 +16,5 @@ Vue.component('md-alert', Alert)
 Vue.component('md-live', Live)
 Vue.component('md-code-block', CodeBlock)
 Vue.component('md-option', OptionLink)
+
+Vue.directive('observe-visibility', ObserveVisibility)
diff --git a/contents/en/basics/release-note/v5-feature.md b/contents/en/basics/release-note/v5-feature.md
index 4db0548..af41b22 100644
--- a/contents/en/basics/release-note/v5-feature.md
+++ b/contents/en/basics/release-note/v5-feature.md
@@ -4,7 +4,7 @@ Data visualization has come a long way in the last few years. Developers no long
 
 Apache ECharts has always been committed to making it easier for developers to create flexible and rich visualizations. In the latest release of Apache ECharts 5, we have focused on enhancing the storytelling of charts, allowing developers to tell the story behind the data in a simpler way.
 
-<img src="images/feature-v5/echarts-5-en.png" width="800px" />
+<img data-src="images/feature-v5/echarts-5-en.png" width="800px" />
 
 "The core of Apache ECharts 5 is "Show, do not tell", which is a comprehensive upgrade of five topics and 15 features around the stroy telling of visualizations, allowing charts to better tell the story behind the data. It helps developers to create visualizations that meet the needs of various scenarios more easily.
 
@@ -16,8 +16,8 @@ The importance of animation to human cognition cannot be overstated. In our prev
 
 Apache ECharts 5 adds support for dynamically sorted bar-racing and dynamically sorted line-racing charts to help developers easily create time-series charts that show changes in data over time and tell the evolution of data.
 
-<md-example src="bar-race-country" width="700" height="400"></md-example>
-<md-example src="line-race" width="700" height="400"></md-example>
+<md-example data-src="bar-race-country" width="700" height="400"></md-example>
+<md-example data-src="line-race" width="700" height="400"></md-example>
 
 The dynamic sorting chart shows the derivation of different categories in the ranking over time. The developer can enable this effect in ECharts with a few simple configuration code.
 
@@ -35,18 +35,18 @@ The role of visual design is not only to make the chart look better, but more im
 
 We have found that a large percentage of developers use the default theme style for ECharts, so it is important to have an elegant default theme design. In Apache ECharts 5, we redesigned the default theme style, optimizing it for different charts and components. For example, we took into account factors such as differentiation between colors, contrast with background colors, and harmony with adjacent colors, and ensured that people with color blindness could clearly distinguish data.
 
-<img src="images/feature-v5/theme-color.png" width="400px" />
+<img data-src="images/feature-v5/theme-color.png" width="400px" />
 
 Let's look at the new version of the light and dark theme styles using the most commonly used bar chart as an example.
 
-<img src="images/feature-v5/new-theme-light.png" width="500px" />
-<img src="images/feature-v5/new-theme-dark.png" width="500px" />
+<img data-src="images/feature-v5/new-theme-light.png" width="500px" />
+<img data-src="images/feature-v5/new-theme-dark.png" width="500px" />
 
 For the data area zoom, timeline and other interactive components, we also designed a new style and provide a better interactive experience:
 
-<img src="images/feature-v5/dataZoom.png" width="500px" />
+<img data-src="images/feature-v5/dataZoom.png" width="500px" />
 
-<img src="images/feature-v5/timeline.png" width="500px" />
+<img data-src="images/feature-v5/timeline.png" width="500px" />
 
 #### Label
 
@@ -60,7 +60,7 @@ We also provide several configuration options to allow developers to actively co
 
 The new label feature allows you to have very clear label presentation even in a confined space mobile:
 
-<img src="images/feature-v5/pie-label.png" width="400px" />
+<img data-src="images/feature-v5/pie-label.png" width="400px" />
 
 #### Time Axis
 
@@ -70,16 +70,16 @@ First of all, the time axis is no longer split absolutely evenly as before, but
 
 The display of the time scale at different dataZoom granularities.
 
-<img src="images/feature-v5/time-axis.png" width="600px" />
+<img data-src="images/feature-v5/time-axis.png" width="600px" />
 
-<img src="images/feature-v5/time-axis-2.png" width="600px" />
+<img data-src="images/feature-v5/time-axis-2.png" width="600px" />
 
 #### Tooltip
 
 Tooltip is one of the most commonly used visualization components to help users interactively understand the details of data. In Apache ECharts 5, we have optimized the style of the tooltip, making the default display of the tooltip elegant and clear by adjusting the font style, color, arrow pointing to the graph, border color following the graph color, and other features. The rendering logic of rich text has been improved to ensure that the display is consistent with the HTML way, allow [...]
 
-<img src="images/feature-v5/new-tooltip.png" width="400px" />
-<img src="images/feature-v5/new-tooltip-2.png" width="400px" />
+<img data-src="images/feature-v5/new-tooltip.png" width="400px" />
+<img data-src="images/feature-v5/new-tooltip-2.png" width="400px" />
 
 In addition to this, we have also added the ability to sort the list in the tip box by value size or category order this time.
 
@@ -89,7 +89,7 @@ We have seen a lot of cool gauge charts created by community users, but the way
 
 Different styles of gauge pointers.
 
-<img src="images/feature-v5/gauge-pointer.png" width="600px" />
+<img data-src="images/feature-v5/gauge-pointer.png" width="600px" />
 
 These upgrades not only allow developers to achieve cool effects with simpler configuration items, but also bring richer customization capabilities.
 
@@ -123,7 +123,7 @@ Apache ECharts 5 has new support for dirty rectangle rendering to address perfor
 
 A visual demonstration of a dirty rectangle, with the red boxed area redrawn for the frame.
 
-<img src="images/feature-v5/dirty-rect.gif" width="500px" />
+<img data-src="images/feature-v5/dirty-rect.gif" width="500px" />
 
 You can see the effect by enable dirty rectangle optimization on the new example page.
 
diff --git a/contents/en/meta/edit-guide.md b/contents/en/meta/edit-guide.md
index e34122f..2ee6686 100644
--- a/contents/en/meta/edit-guide.md
+++ b/contents/en/meta/edit-guide.md
@@ -254,7 +254,7 @@ Source images are stored under `static/images/`.
 For the temporary style of the current page, you can just write html.
 
 ```markdown
-<img src="images/demo.png" style="width: 50px" />
+<img data-src="images/demo.png" style="width: 50px" />
 ```
 
 ## Add Example Iframe
diff --git a/contents/zh/basics/release-note/v5-feature.md b/contents/zh/basics/release-note/v5-feature.md
index d7dc528..c148d9c 100644
--- a/contents/zh/basics/release-note/v5-feature.md
+++ b/contents/zh/basics/release-note/v5-feature.md
@@ -4,7 +4,7 @@
 
 Apache ECharts 始终致力于让开发者以更方便的方式创造灵活丰富的可视化作品。在最新推出的 Apache ECharts 5,我们着力加强了图表的叙事能力,让开发者可以以更简单的方式,讲述数据背后的故事。
 
-<img src="images/feature-v5/echarts-5.png" width="800px" />
+<img data-src="images/feature-v5/echarts-5.png" width="800px" />
 
 “表·达”是 Apache ECharts 5 的核心,通过五大模块、十五项特性的全面升级,围绕可视化作品的叙事表达能力,让图“表”更能传“达”数据背后的故事,帮助开发者更轻松地创造满足各种场景需求的可视化作品。
 
@@ -35,18 +35,18 @@ Apache ECharts 5 新增支持动态排序柱状图(bar-racing)以及动态
 
 我们发现,有很大一部分开发者使用了 ECharts 默认的主题样式,因而设计优雅、符合可视化原理的默认主题设计是非常重要的。在 Apache ECharts 5 中,我们重新设计了默认的主题样式,针对不同的系列和组件分别做了优化调整。以主题色为例,我们考量了颜色之间的区分度、与背景色的对比度、相邻颜色的和谐度等因素,并且确保色觉辨识障碍人士也能清楚地区分数据。
 
-<img src="images/feature-v5/theme-color.png" width="400px" />
+<img data-src="images/feature-v5/theme-color.png" width="400px" />
 
 我们以最常用的柱状图为例,来看看新版本浅色主题和深色主题的样式:
 
-<img src="images/feature-v5/new-theme-light.png" width="500px" />
-<img src="images/feature-v5/new-theme-dark.png" width="500px" />
+<img data-src="images/feature-v5/new-theme-light.png" width="500px" />
+<img data-src="images/feature-v5/new-theme-dark.png" width="500px" />
 
 对于数据区域缩放,时间轴等交互组件,我们也设计了全新的样式并且提供了更好的交互体验:
 
-<img src="images/feature-v5/dataZoom.png" width="500px" />
+<img data-src="images/feature-v5/dataZoom.png" width="500px" />
 
-<img src="images/feature-v5/timeline.png" width="500px" />
+<img data-src="images/feature-v5/timeline.png" width="500px" />
 
 #### 标签
 
@@ -60,7 +60,7 @@ Apache ECharts 5 可以通过一个配置项开启自动隐藏重叠的标签。
 
 新的标签功能可以让你在移动端这样局限的空间内也可以有很优雅的标签展示:
 
-<img src="images/feature-v5/pie-label.png" width="400px" />
+<img data-src="images/feature-v5/pie-label.png" width="400px" />
 
 #### 时间轴
 
@@ -70,16 +70,16 @@ Apache ECharts 5 带来了适于表达时间标签刻度的时间轴。时间轴
 
 不同的 dataZoom 粒度下时间刻度的显示:
 
-<img src="images/feature-v5/time-axis.png" width="600px" />
+<img data-src="images/feature-v5/time-axis.png" width="600px" />
 
-<img src="images/feature-v5/time-axis-2.png" width="600px" />
+<img data-src="images/feature-v5/time-axis-2.png" width="600px" />
 
 #### 提示框
 
 提示框(Tooltip)是一种最常用的可视化组件,可以帮助用户交互式地了解数据的详细信息。在 Apache ECharts 5 中,我们对提示框的样式进行了优化,通过对字体样式,颜色的调整,指向图形的箭头,跟随图形颜色的边框色等功能,让提示框的默认展示优雅又清晰。并且改进了富文本的渲染逻辑,确保显示效果与 HTML 方式一致,让用户在不同场景下可以选择不同的技术方案实现同样的效果。
 
-<img src="images/feature-v5/new-tooltip.png" width="400px" />
-<img src="images/feature-v5/new-tooltip-2.png" width="400px" />
+<img data-src="images/feature-v5/new-tooltip.png" width="400px" />
+<img data-src="images/feature-v5/new-tooltip-2.png" width="400px" />
 
 除此之外,我们这次也加上了提示框内的列表按照数值大小或者类目顺序排序的功能。
 
@@ -89,7 +89,7 @@ Apache ECharts 5 带来了适于表达时间标签刻度的时间轴。时间轴
 
 不同样式的仪表盘指针:
 
-<img src="images/feature-v5/gauge-pointer.png" width="600px" />
+<img data-src="images/feature-v5/gauge-pointer.png" width="600px" />
 
 这些升级,不仅可以让开发者用更简单的配置项实现酷炫的效果,而且带来了更丰富的定制能力。
 
@@ -123,7 +123,7 @@ Apache ECharts 5 新支持了脏矩形渲染,解决只有局部变化的场景
 
 脏矩形的可视化演示,红色框选部分为该帧重绘区域:
 
-<img src="images/feature-v5/dirty-rect.gif" width="500px" />
+<img data-src="images/feature-v5/dirty-rect.gif" width="500px" />
 
 大家在新的示例页面选择开启脏矩形优化就可以看到该效果。
 
diff --git a/contents/zh/concepts/data-transform.md b/contents/zh/concepts/data-transform.md
index 1c6e26f..a75a27f 100644
--- a/contents/zh/concepts/data-transform.md
+++ b/contents/zh/concepts/data-transform.md
@@ -13,7 +13,7 @@ Apache ECharts<sup>TM</sup> 5 开始支持了“数据转换”( data transfor
 
 ## 数据转换基础使用
 
-在 echarts 中,数据转换是依托于数据集([dataset](${optionPath}#dataset))来实现的. 我们可以设置 [dataset.transform](${optionPath}#dataset.transform) 来表示,此 dataset 的数据,来自于此 transform 的结果。例如。
+在 echarts 中,数据转换是依托于数据集([dataset](${optionPath}#dataset))来实现的. 我们可以设置 [dataset.transform](${optionPath}#dataset.transform) 来表示,此 dataset 的数据,来自于此 transform 的结果。
 
 下面是上述例子的效果,三个饼图分别显示了 2011、2012、2013 年的数据。
 
diff --git a/contents/zh/meta/edit-guide.md b/contents/zh/meta/edit-guide.md
index d0b5cb2..f37dbe8 100644
--- a/contents/zh/meta/edit-guide.md
+++ b/contents/zh/meta/edit-guide.md
@@ -243,7 +243,7 @@ option = {
 对于当前页面的临时样式,可以直接写 html:
 
 ```markdown
-<img src="images/demo.png" style="width: 50px" />
+<img data-src="images/demo.png" style="width: 50px" />
 ```
 
 ## 添加示例 iframe
diff --git a/package-lock.json b/package-lock.json
index b9b47f6..7c76a35 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5391,14 +5391,6 @@
       "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
       "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4="
     },
-    "ansi-red": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz",
-      "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=",
-      "requires": {
-        "ansi-wrap": "0.1.0"
-      }
-    },
     "ansi-regex": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
@@ -5412,11 +5404,6 @@
         "color-convert": "^1.9.0"
       }
     },
-    "ansi-wrap": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
-      "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768="
-    },
     "any-promise": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@@ -5620,14 +5607,6 @@
       "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
       "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
     },
-    "autolinker": {
-      "version": "0.28.1",
-      "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz",
-      "integrity": "sha1-BlK0kYgYefB3XazgzcoyM5QqTkc=",
-      "requires": {
-        "gulp-header": "^1.7.1"
-      }
-    },
     "autoprefixer": {
       "version": "9.8.6",
       "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz",
@@ -6817,11 +6796,6 @@
       "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
       "dev": true
     },
-    "coffee-script": {
-      "version": "1.12.7",
-      "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz",
-      "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw=="
-    },
     "collection-visit": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@@ -6968,21 +6942,6 @@
         }
       }
     },
-    "concat-with-sourcemaps": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz",
-      "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==",
-      "requires": {
-        "source-map": "^0.6.1"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
-        }
-      }
-    },
     "connect": {
       "version": "3.7.0",
       "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
@@ -7912,11 +7871,6 @@
       "resolved": "https://registry.npmjs.org/devalue/-/devalue-2.0.1.tgz",
       "integrity": "sha512-I2TiqT5iWBEyB8GRfTDP0hiLZ0YeDJZ+upDxjBfOC2lebO5LezQMv7QvIUTzdb64jQyAKLf1AHADtGN+jw6v8Q=="
     },
-    "diacritics-map": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz",
-      "integrity": "sha1-bfwP+dAQAKLt8oZTccrDFulJd68="
-    },
     "didyoumean": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz",
@@ -9009,52 +8963,6 @@
         }
       }
     },
-    "expand-range": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
-      "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
-      "requires": {
-        "fill-range": "^2.1.0"
-      },
-      "dependencies": {
-        "fill-range": {
-          "version": "2.2.4",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
-          "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==",
-          "requires": {
-            "is-number": "^2.1.0",
-            "isobject": "^2.0.0",
-            "randomatic": "^3.0.0",
-            "repeat-element": "^1.1.2",
-            "repeat-string": "^1.5.2"
-          }
-        },
-        "is-number": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
-          "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
-          "requires": {
-            "kind-of": "^3.0.2"
-          }
-        },
-        "isobject": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-          "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
-          "requires": {
-            "isarray": "1.0.0"
-          }
-        },
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
     "extend": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -10584,38 +10492,6 @@
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
       "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
     },
-    "gray-matter": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz",
-      "integrity": "sha1-MELZrewqHe1qdwep7SOA+KF6Qw4=",
-      "requires": {
-        "ansi-red": "^0.1.1",
-        "coffee-script": "^1.12.4",
-        "extend-shallow": "^2.0.1",
-        "js-yaml": "^3.8.1",
-        "toml": "^2.3.2"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
-      }
-    },
-    "gulp-header": {
-      "version": "1.8.12",
-      "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz",
-      "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==",
-      "requires": {
-        "concat-with-sourcemaps": "*",
-        "lodash.template": "^4.4.0",
-        "through2": "^2.0.0"
-      }
-    },
     "gzip-size": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
@@ -11211,6 +11087,11 @@
         }
       }
     },
+    "intersection-observer": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.0.tgz",
+      "integrity": "sha512-2Vkz8z46Dv401zTWudDGwO7KiGHNDkMv417T5ItcNYfmvHR/1qCTVBO9vwH8zZmQ0WkA/1ARwpysR9bsnop4NQ=="
+    },
     "ip": {
       "version": "1.1.5",
       "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@@ -11822,14 +11703,6 @@
         "launch-editor": "^2.2.1"
       }
     },
-    "lazy-cache": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz",
-      "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=",
-      "requires": {
-        "set-getter": "^0.1.0"
-      }
-    },
     "levn": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@@ -11854,43 +11727,6 @@
         "uc.micro": "^1.0.1"
       }
     },
-    "list-item": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz",
-      "integrity": "sha1-DGXQDih8tmPMs8s4Sad+iewmilY=",
-      "requires": {
-        "expand-range": "^1.8.1",
-        "extend-shallow": "^2.0.1",
-        "is-number": "^2.1.0",
-        "repeat-string": "^1.5.2"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "is-number": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
-          "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
-          "requires": {
-            "kind-of": "^3.0.2"
-          }
-        },
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
     "load-json-file": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
@@ -12218,40 +12054,6 @@
       "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.1.2.tgz",
       "integrity": "sha512-9D58TKK4dakqmjcmVuqHoB3ntKBpQJ0Ld38B83aiHJcBD72IZIyPjNtihPA6ayRI5WD33e1W68mArliNLHCprg=="
     },
-    "markdown-it-table-of-contents": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.5.2.tgz",
-      "integrity": "sha512-6o+rxSwzXmXCUn1n8QGTSpgbcnHBG6lUU8x7A5Cssuq5vbfzTfitfGPvQ5PZkp+gP1NGS/DR2rkYqJPn0rbZ1A=="
-    },
-    "markdown-link": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz",
-      "integrity": "sha1-MsXGUZmmRXMWMi0eQinRNAfIx88="
-    },
-    "markdown-toc": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/markdown-toc/-/markdown-toc-1.2.0.tgz",
-      "integrity": "sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg==",
-      "requires": {
-        "concat-stream": "^1.5.2",
-        "diacritics-map": "^0.1.0",
-        "gray-matter": "^2.1.0",
-        "lazy-cache": "^2.0.2",
-        "list-item": "^1.1.1",
-        "markdown-link": "^0.1.1",
-        "minimist": "^1.2.0",
-        "mixin-deep": "^1.1.3",
-        "object.pick": "^1.2.0",
-        "remarkable": "^1.7.1",
-        "repeat-string": "^1.6.1",
-        "strip-color": "^0.1.0"
-      }
-    },
-    "math-random": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz",
-      "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A=="
-    },
     "md5.js": {
       "version": "1.3.5",
       "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -16880,23 +16682,6 @@
       "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
       "dev": true
     },
-    "randomatic": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz",
-      "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==",
-      "requires": {
-        "is-number": "^4.0.0",
-        "kind-of": "^6.0.0",
-        "math-random": "^1.0.1"
-      },
-      "dependencies": {
-        "is-number": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
-          "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ=="
-        }
-      }
-    },
     "randombytes": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -17150,15 +16935,6 @@
       "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
       "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
     },
-    "remarkable": {
-      "version": "1.7.4",
-      "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz",
-      "integrity": "sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==",
-      "requires": {
-        "argparse": "^1.0.10",
-        "autolinker": "~0.28.0"
-      }
-    },
     "remove-trailing-separator": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@@ -17761,14 +17537,6 @@
       "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
       "dev": true
     },
-    "set-getter": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.1.tgz",
-      "integrity": "sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==",
-      "requires": {
-        "to-object-path": "^0.3.0"
-      }
-    },
     "set-value": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
@@ -18387,11 +18155,6 @@
       "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
       "dev": true
     },
-    "strip-color": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz",
-      "integrity": "sha1-EG9l09PmotlAHKwOsM6LinArT3s="
-    },
     "strip-final-newline": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
@@ -19182,11 +18945,6 @@
       "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
       "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
     },
-    "toml": {
-      "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz",
-      "integrity": "sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ=="
-    },
     "totalist": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz",
@@ -19769,6 +19527,12 @@
         "spdx-expression-parse": "^3.0.0"
       }
     },
+    "vanilla-lazyload": {
+      "version": "17.4.0",
+      "resolved": "https://registry.npmjs.org/vanilla-lazyload/-/vanilla-lazyload-17.4.0.tgz",
+      "integrity": "sha512-4IVX93uLEgQGBdSCaSE1XJgeNNT1+sV8PKiImM21EkDGJBH3tTWL9wYrMA4xT5C2EIEfw8WkinUxUG+uMY2ZyA==",
+      "dev": true
+    },
     "vary": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -19873,6 +19637,11 @@
       "resolved": "https://registry.npmjs.org/vue-no-ssr/-/vue-no-ssr-1.1.1.tgz",
       "integrity": "sha512-ZMjqRpWabMPqPc7gIrG0Nw6vRf1+itwf0Itft7LbMXs2g3Zs/NFmevjZGN1x7K3Q95GmIjWbQZTVerxiBxI+0g=="
     },
+    "vue-observe-visibility": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/vue-observe-visibility/-/vue-observe-visibility-1.0.0.tgz",
+      "integrity": "sha512-s5TFh3s3h3Mhd3jaz3zGzkVHKHnc/0C/gNr30olO99+yw2hl3WBhK3ng3/f9OF+qkW4+l7GkmwfAzDAcY3lCFg=="
+    },
     "vue-prism-editor": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/vue-prism-editor/-/vue-prism-editor-1.2.2.tgz",
diff --git a/package.json b/package.json
index 4f366f4..5c39dda 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
     "@nuxt/types": "^2.15.6",
     "@vue/composition-api": "^1.0.0-rc.10",
     "clipboard": "^2.0.8",
+    "intersection-observer": "^0.12.0",
     "js-base64": "^3.6.1",
     "js-yaml-loader": "^1.2.2",
     "lodash": "^4.17.21",
@@ -25,6 +26,7 @@
     "prism-themes": "^1.7.0",
     "prismjs": "^1.24.1",
     "scroll-into-view": "^1.15.0",
+    "vue-observe-visibility": "^1.0.0",
     "vue-prism-editor": "^1.2.2"
   },
   "devDependencies": {
@@ -46,6 +48,7 @@
     "raw-loader": "^4.0.0",
     "resize-detector": "^0.3.0",
     "sass-loader": "^8.0.2",
+    "vanilla-lazyload": "^17.4.0",
     "webpack": "^4.42.1"
   }
 }
diff --git a/pages/_.vue b/pages/_.vue
index 115e7c7..5704dde 100644
--- a/pages/_.vue
+++ b/pages/_.vue
@@ -28,6 +28,7 @@ import Contributors from '~/components/partials/Contributors.vue'
 import PostContent from '~/components/partials/PostContent'
 import * as base64 from 'js-base64'
 import config from '~/configs/config'
+import LazyLoad from 'vanilla-lazyload'
 
 function parseLiveCodeBlocks(md: string) {
   return md.replace(
@@ -102,6 +103,20 @@ export default Vue.extend({
         id: slugify(title)
       })
     }
+    setTimeout(() => {
+      // FIXME not sure why this needs to be in the setTimeout
+      // init lazy load
+      // @ts-ignore
+      this._lazyload = new LazyLoad({
+        // container: this.$el.querySelector('.post-inner') as HTMLElement,
+        elements_selector: 'img[data-src], iframe[data-src]',
+        threshold: 300
+      })
+    })
+  },
+  destroyed() {
+    // @ts-ignore
+    this._lazyload && this._lazyload.destroy()
   },
   async asyncData({ $content, params, i18n, $el }: any) {
     const postPath = `${i18n.locale}/${params.pathMatch}`
@@ -114,13 +129,24 @@ export default Vue.extend({
     const md = markdown({
       html: true,
       linkify: true
-    }).use(anchor, {
-      // slugify,
-      permalink: false,
-      permalinkAfter: true,
-      permalinkSymbol: '#',
-      permalinkClass: 'permalink'
     })
+      .use(anchor, {
+        // slugify,
+        permalink: false,
+        permalinkAfter: true,
+        permalinkSymbol: '#',
+        permalinkClass: 'permalink'
+      })
+      .use(function(md) {
+        const defaultImageRenderer = md.renderer.rules.image
+        md.renderer.rules.image = function(tokens, idx, options, env, self) {
+          const token = tokens[idx]
+          const srcValue = token.attrGet('src')
+          token.attrPush(['data-src', srcValue])
+          token.attrSet('src', '')
+          return defaultImageRenderer(tokens, idx, options, env, self)
+        }
+      }) // lazyload
 
     return { html: md.render(content), postPath }
   }

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org