# Animated Echarts with Panel

**URL:** <https://discourse.holoviz.org/t/animated-echarts-with-panel/6129>\
**Category:** Panel\
**Tags:** echarts\
**Created:** [September 30, 2023, 5:29pm UTC](https://discourse.holoviz.org/t/animated-echarts-with-panel/6129 "2023-09-30T17:29:24Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![svvoronin](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/svvoronin/32/3892_2.png) [@svvoronin](https://discourse.holoviz.org/u/svvoronin)\
**Post date:** [September 30, 2023, 5:29pm UTC](https://discourse.holoviz.org/t/animated-echarts-with-panel/6129/1 "2023-09-30T17:29:24Z")

</div>

Dear Community,

I am currently in the process of creating dashboards using Echarts within Panel. I truly like Echarts for its styling, animations and extensive configurability. The Apache Echarts gallery, which can be found at [Examples - Apache ECharts](https://echarts.apache.org/examples/en/index.html), provides a wide range of excellent examples for various chart types. Thanks to Panel’s Echarts rendering Apache Echarts, most of these examples can be seamlessly integrated into Panel’s pane.

However, Panel currently doesn’t support javascript function with the ECharts pane particularly for more advanced charts. As a workaround, some suggestions were made to modify formatter parameters of the chart [Display negative numbers as positive numbers in bar type ECharts](https://discourse.holoviz.org/t/display-negative-numbers-as-positive-numbers-in-bar-type-echarts/4236).

I am now wondering whether it’s feasible to integrate animated ECharts, such as the Line Race ([Examples - Apache ECharts](https://echarts.apache.org/examples/en/editor.html?c=line-race)) or Bar Race ([Examples - Apache ECharts](https://echarts.apache.org/examples/en/editor.html?c=bar-race)) charts with animationDuration, setTimeout and setInterval parameters into Panel template with Panel Echarts or pyecharts.  
Thank you in advance for any hints!

Br, Sergey

---

<div class="post-metadata">

**Author:** ![svvoronin](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/svvoronin/32/3892_2.png) [@svvoronin](https://discourse.holoviz.org/u/svvoronin)\
**Post date:** [October 1, 2023, 6:12pm UTC](https://discourse.holoviz.org/t/animated-echarts-with-panel/6129/2 "2023-10-01T18:12:48Z")

</div>

I managed to get Bar Race with pn.pane.HTML()

```auto
import panel as pn

pn.extension()
# js_files = {"echarts5": "https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"}
# pn.extension(js_files=js_files)

bar_color = "green"
html = f"""
    <body style="height: 100%; margin: 0">
    
        <div id="main" style="height: 100%"></div>
        <script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <!-- Include jQuery -->
        <script type="text/javascript">

var option;

const data = [];
for (let i = 0; i < 5; ++i) {{
  data.push(Math.round(Math.random() * 200));
}}
option = {{
  xAxis: {{
    max: 'dataMax'
  }},
  yAxis: {{
    type: 'category',
    data: ['A', 'B', 'C', 'D', 'E'],
    inverse: true,
    animationDuration: 300,
    animationDurationUpdate: 300,
    max: 2 // only the largest 3 bars will be displayed
  }},
  series: [
    {{
      realtimeSort: true,
      name: 'X',
      type: 'bar',
      data: data,
      label: {{
        show: true,
        position: 'right',
        valueAnimation: true
      }},
      itemStyle: {{
        color: '{bar_color}' 
      }}
    }}
  ],
  legend: {{
    show: true
  }},
  animationDuration: 0,
  animationDurationUpdate: 3000,
  animationEasing: 'linear',
  animationEasingUpdate: 'linear'
}};
function run() {{
  for (var i = 0; i < data.length; ++i) {{
    if (Math.random() > 0.9) {{
      data[i] += Math.round(Math.random() * 2000);
    }} else {{
      data[i] += Math.round(Math.random() * 200);
    }}
  }}
  myChart.setOption({{
    series: [
      {{
        type: 'bar',
        data
      }}
    ]
  }});
}}

setInterval(function () {{
  run();
}}, 3000);

var chartDom = document.getElementById("main");
var myChart;

function initializeChart() {{
    myChart = echarts.init(chartDom);
    if (option && typeof option === 'object') {{
        myChart.setOption(option);
    }}

    setTimeout(function () {{
    run();
    }}, 0);
}}

var script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js'; // Specify the ECharts version
script.onload = initializeChart;
document.head.appendChild(script);

        </script>
    </body>
"""

bar_race = pn.pane.HTML(html, width=500, height=500)
tmpl = pn.template.MaterialTemplate(title="Bar Race")
tmpl.main.append(bar_race)
tmpl.servable()

```

[/uploads/default/original/2X/5/5aa96dc389df289f66b434983ff682a5027f951d.mp4](https://discourse.holoviz.org/uploads/default/original/2X/5/5aa96dc389df289f66b434983ff682a5027f951d.mp4)

However, when I attempt to incorporate a widget for altering the color of the bar, the widget doesn’t appear visible. It appears that the chart is covering up all other content. This issue might be related to the “id” attribute being set to “main” in the JavaScript script. However, altering the ID name results in an error: “Uncaught TypeError: Cannot read properties of null (reading ‘getAttribute’).”

```auto
import panel as pn

pn.extension()
# js_files = {"echarts5": "https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"}
# pn.extension(js_files=js_files)

bar_color = pn.widgets.RadioButtonGroup(
    name="Select Color",
    options=["green", "red", "blue"],
    button_type="success",
    button_style="outline",
    orientation="horizontal",
)

@pn.depends(bar_color.param.value)
def change_bar_color(color):
    html = f"""
        <body style="height: 100%; margin: 0">
        
            <div id="main" style="height: 100%"></div>
            <script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <!-- Include jQuery -->
            <script type="text/javascript">

    var option;

    const data = [];
    for (let i = 0; i < 5; ++i) {{
    data.push(Math.round(Math.random() * 200));
    }}
    option = {{
    xAxis: {{
        max: 'dataMax'
    }},
    yAxis: {{
        type: 'category',
        data: ['A', 'B', 'C', 'D', 'E'],
        inverse: true,
        animationDuration: 300,
        animationDurationUpdate: 300,
        max: 2 // only the largest 3 bars will be displayed
    }},
    series: [
        {{
        realtimeSort: true,
        name: 'X',
        type: 'bar',
        data: data,
        label: {{
            show: true,
            position: 'right',
            valueAnimation: true
        }},
        itemStyle: {{
            color: '{color}' 
        }}
        }}
    ],
    legend: {{
        show: true
    }},
    animationDuration: 0,
    animationDurationUpdate: 3000,
    animationEasing: 'linear',
    animationEasingUpdate: 'linear'
    }};
    function run() {{
    for (var i = 0; i < data.length; ++i) {{
        if (Math.random() > 0.9) {{
        data[i] += Math.round(Math.random() * 2000);
        }} else {{
        data[i] += Math.round(Math.random() * 200);
        }}
    }}
    myChart.setOption({{
        series: [
        {{
            type: 'bar',
            data
        }}
        ]
    }});
    }}

    setInterval(function () {{
    run();
    }}, 3000);

    var chartDom = document.getElementById("main");
    var myChart;

    function initializeChart() {{
        myChart = echarts.init(chartDom);
        if (option && typeof option === 'object') {{
            myChart.setOption(option);
        }}

        setTimeout(function () {{
        run();
        }}, 0);
    }}

    var script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js'; // Specify the ECharts version
    script.onload = initializeChart;
    document.head.appendChild(script);

            </script>
        </body>
    """
    return pn.pane.HTML(html, width=500, height=500)

bar_race = pn.bind(change_bar_color, bar_color)

tmpl = pn.template.MaterialTemplate(title="Bar Race")
tmpl.main.append(pn.Row(bar_color, bar_race))
tmpl.servable()

```

 ![image](https://discourse.holoviz.org/uploads/default/original/2X/9/98fe67eb50b317152b7696b97f2e6d5f85c5e12b.png)

---

<div class="post-metadata">

**Author:** ![Marc](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/marc/32/24_2.png) [@Marc](https://discourse.holoviz.org/u/Marc)\
**Post date:** [October 3, 2023, 3:54am UTC](https://discourse.holoviz.org/t/animated-echarts-with-panel/6129/3 "2023-10-03T03:54:07Z")

</div>

Hi @svvoronin

Different things are causing the error.

- The html document already contains an element with id `main`. I.e. this is the element you select with `document.getElementByID('main')`. And that is why the widget gets removed.
- With Panel 1.x your HTML pane is rendering inside shadow dom. So getting handles on the `div` element you want to render the chart in is not straightforward.

I might be able to work with the script and get it fixed. But instead of using the `HTML` pane you should be using the `ReactiveHTML` component. Its made for use cases like these.

I will try to develop an example and share it.

---

<div class="post-metadata">

**Author:** ![svvoronin](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/svvoronin/32/3892_2.png) [@svvoronin](https://discourse.holoviz.org/u/svvoronin)\
**Post date:** [October 3, 2023, 8:56am UTC](https://discourse.holoviz.org/t/animated-echarts-with-panel/6129/4 "2023-10-03T08:56:47Z")

</div>

Hi @Marc,

Thank you for looking into this matter. Your help is highly appreciated.

Sergey

---

<div class="post-metadata">

**Author:** ![Marc](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/marc/32/24_2.png) [@Marc](https://discourse.holoviz.org/u/Marc)\
**Post date:** [October 6, 2023, 5:37pm UTC](https://discourse.holoviz.org/t/animated-echarts-with-panel/6129/5 "2023-10-06T17:37:49Z")

</div>

Hi @svvoronin

Here is an example using `ReactiveHTML`.

[/uploads/default/original/2X/5/53ea503d119ce4aa12c485997974ae8e8fc779c5.mp4](https://discourse.holoviz.org/uploads/default/original/2X/5/53ea503d119ce4aa12c485997974ae8e8fc779c5.mp4)

```python
import panel as pn
import param
from panel.reactive import ReactiveHTML

pn.extension("echarts")
pn.pane.ECharts

class CustomComponent(ReactiveHTML):
    color=param.Color("pink")
    _template = """<div id="chartDom" style="height: 100%;width:100%"></div>"""
    _scripts = {"render": """
const plot_data = [];
for (let i = 0; i < 5; ++i) {
    plot_data.push(Math.round(Math.random() * 200));
}
const option = {
    xAxis: {
    max: 'dataMax'
    },
    yAxis: {
    type: 'category',
    data: ['A', 'B', 'C', 'D', 'E'],
    inverse: true,
    animationDuration: 300,
    animationDurationUpdate: 300,
    max: 2 // only the largest 3 bars will be displayed
    },
    series: [
    {
    realtimeSort: true,
    name: 'X',
    type: 'bar',
    data: plot_data,
    label: {
        show: true,
        position: 'right',
        valueAnimation: true
    },
    itemStyle: {
        color: data.color
    }
    }
    ],
    legend: {
    show: true
    },
    animationDuration: 0,
    animationDurationUpdate: 3000,
    animationEasing: 'linear',
    animationEasingUpdate: 'linear'
};

const config = {width: model.width, height: model.height}
state.myChart = echarts.init(chartDom,null,config);
state.myChart.setOption(option);
setInterval(function () {self.run();}, 3000);
""",
    "after_layout": "state.myChart.resize();self.run();",
    "run": """
const plot_data = [];
for (let i = 0; i < 5; ++i) {
    plot_data.push(Math.round(Math.random() * 200));
}
const option={series: [{data: plot_data}]}
state.myChart.setOption(option);
""",
    "color": """
option={series: [{itemStyle: {color: data.color}}]}
state.myChart.setOption(option)
"""
    }
component = CustomComponent(height=500, sizing_mode="stretch_width")
pn.Column(
    pn.widgets.RadioButtonGroup.from_param(
        component.param.color,
        name="Select Color",
        value="green",
        options=["green", "red", "blue"],
        button_type="success",
        button_style="outline",
        orientation="horizontal",
    ),
    component
).servable()

```

You can learn more about `ReactiveHTML` in

- [Build Components from Scratch — Panel v1.2.3 (holoviz.org)](https://panel.holoviz.org/how_to/custom_components/custom_reactiveHTML.html)
- [Building Custom Components — Panel v1.2.3 (holoviz.org)](https://panel.holoviz.org/explanation/components/components_custom.html)

I’m working on much improved `ReactiveHTML` documentation here

[Refactor ReactiveHTML docs by MarcSkovMadsen · Pull Request #5448 · holoviz/panel (github.com)](https://github.com/holoviz/panel/pull/5448)

---

<div class="post-metadata">

**Author:** ![svvoronin](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/svvoronin/32/3892_2.png) [@svvoronin](https://discourse.holoviz.org/u/svvoronin)\
**Post date:** [October 6, 2023, 6:14pm UTC](https://discourse.holoviz.org/t/animated-echarts-with-panel/6129/6 "2023-10-06T18:14:04Z")

</div>

Hi @Marc,

Awesome! Thanks for the fix and the links to the documentation - it’s extremely helpful.

Sergey
