Pre loader

Category: JavaScript

Welcome to the SciChart Forums!

  • Please read our Question Asking Guidelines for how to format a good question
  • Some reputation is required to post answers. Get up-voted to avoid the spam filter!
  • We welcome community answers and upvotes. Every Q&A improves SciChart for everyone

WPF Forums | JavaScript Forums | Android Forums | iOS Forums

1 vote
5k views

I am trying to implement the following functionality requested by our users:

Zoom:
– Only zoom the chart when using CTRL + MouseWheel key
– This is similar to how most browsers behave

Pan/Scroll:
– If no CTRL key is pressed, then pan/scroll
– This is similar to how most browsers behave

Given the above requirement, I started by creating custom modifier by extending YAxisDragModifier.

My code almost works:
– When using CTRL + Mouse Left & Drag, it Scales (zooms) — just like the user wants
– But using CTRL + Mouse Wheel does not do the same — it only Scales (Zooms).

I have uploaded the code at codesandbox.io:
https://codesandbox.io/s/amazing-shape-2ispt0?file=/src/customModifiers.ts

Note that I have not tried to implement this in ZoomPanModifier yet, but would be helpful if you could give me some pointers on getting this feature in there too.

I would appreciate any help I can get and thanks a bunch in advance.

Best Regards,
Sachin Patel.

1 vote
2k views

Hello All,

Is there a way to have a cursor modifier display on a mouse left/center/right click instead of automatically showing when the mouse is over the chart? I tried setting the ExecuteOn property but had no effect.

Thanks in advance,
Sergio.

1 vote
2k views

I’m a C# developer looking to evaluate working with SciChartJS for a possible future project.

I’ve created a .NET Blazor Project working with JavaScript Interop and I would like to extend the CustomChartModifier2D in TypeScript.

I’ve not used npm to install SciChart into the project, rather I’m using the CDN approach in my _Host.cshtml file.

<script src="https://cdn.jsdelivr.net/npm/[email protected]/index.min.js" crossorigin="anonymous"></script>

I’ve been searching online for a SciChartJS TypeScript definition file so that I can leverage TS Strongly Typed system in my test project.

Is there one? Or do I need to build my own, or even is there an already made example of extending SciChart with TypeScript somewhere I’ve missed?

Thank you

  • David P asked 6 months ago
  • last active 5 months ago
1 vote
6k views

Hi,

Our website is running on a web hosting service supported by the university. To work with the SciChart JS, do we have to install the npm and configure it accordingly? If so, we probably have to contact IT department for assistance, since we don’t have permission to do so.

Please advise. Thanks.

  • Gang Xu asked 3 years ago
  • last active 9 months ago
3 votes
4k views

Hi,

In my application I am using two charts for represent different values. I am using LegendModifier on the first chart. Is it possible when i uncheck the series on first chart and the same color series on second chart also need to hide from the second chart. Any event callback function available in LegendModifier ?

1 vote
5k views

Good afternoon. My team and I are looking at your library to create a depth chart, like here, but have run into a few problems:
1. if we have, for example, a “buySeries” array larger than the “sellSeries” array, the graph is not drawn very nicely.
2. the labels are not drawn correctly when we have too small numbers, I tried to solve the problem with xAxis.textFormatting = ‘0.#####’ or ‘0.00###’, but that doesn’t work for some reason.
3. If we want to add labels while hovering over the chart with showLabel = true , then we get 4 labels, two on yAxis. is it possible to make the buySeries label appear only on the left, for example, and the sellSeries label only on the right? (second screenshot)

1 vote
5k views

I have a big problem with memory, i try many things but it’s doesn’t effect.
It’s my code for testing memory :

    const { wasmContext, sciChartSurface } = await SciChartSurface.create("bandTest", { theme: new SciChartJSLightTheme() });
    sciChartSurface.title = "Axis 1";
    sciChartSurface.xAxes.add(new NumericAxis(wasmContext, { axisTitle: "Column number", axisTitleStyle: {fontSize: 12} }));
    sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { axisAlignment: EAxisAlignment.Left, axisTitleStyle: {fontSize: 12} }));
    setTimeout(sciChartSurface.delete(),2000);

sciChartSurface.delete() have no effect for memory
I run snapshot without this code and another snapshot with this code. (snap 14 is before call SciChart and snap 15 is after call the code bellow)

What can I do?

Regards

1 vote
2k views

I am using the solution you provided on, https://www.scichart.com/questions/js/is-it-possible-to-create-info-box-that-will-sync-with-the-xaxis-of-the-chart.

The solution work great but there is one issue, the issue is that the text annotation should always be visible.

The issue with this solution is that the text annotation disappear if I zoom in somewhere not the text area.

Also another issue is that, in this example, the first info box x1 is beyond the current range, and so the text annotation is not visible, since the x1 of the text annotation is the same as info box and x1 is not within the current range.

First image show the first red info box x1 is not within current range, therefore not showing text annotation and the second info box working as expected with text annotation.

The second image show what happen once I zoom in on the right side away from the text annotation, then the ‘#21H’ is not visible anymore, which is wrong. Just like in my previous question example, the text annotation should always be visible, other than that, everything work great.

Thank you and let me know if there is any question.

  • Nung Khual asked 7 months ago
  • last active 7 months ago
1 vote
6k views

Hello

My application environment is on the mobile browser, so we need to switch between [pan] and [rollover]

When I use a button to switch, everything is fine, but when I want to switch with a long press, an exception occurs

Below is my code

    initModifier() {
        this.partitionList.forEach((obj, idx)=>{
            let sciChartSurface = this.sciObj[idx].sciChartSurface;
            this.zoomPanModifier[idx] = new ZoomPanModifier();
            this.rolloverModifier[idx] = new RolloverModifier({modifierGroup: this.modifierGroupId, showTooltip: false});
            this.zoomPanModifier[idx].isEnabled = true;
            // 擴增功能
            sciChartSurface.chartModifiers.add(
                this.zoomPanModifier[idx],
                new ZoomExtentsModifier(),
                new MouseWheelZoomModifier(),
                new PinchZoomModifier(),
            );
        });
    },
    switchCross() {
        let enablePan = !this.zoomPanModifier[0].isEnabled;
        this.partitionList.forEach((obj, idx)=>{
            let sciChartSurface = this.sciObj[idx].sciChartSurface;
            this.zoomPanModifier[idx].isEnabled = enablePan;
            if (enablePan)
                sciChartSurface.chartModifiers.removeAt(4);
            else
                sciChartSurface.chartModifiers.add(this.rolloverModifier[idx]);
        });
    },

I recorded a video, first use the button to switch, and then long press to switch, you can see the problem I want to narrate from the video, the URL is as follows: https://youtu.be/vJjbLNGS-iM

After the problem occurred, it was expected that touchmove should be [pan], but it became [zoom]

Thanks for your help

1 vote
2k views

Hello,

We have a heatmap where we can zoom in using the MouseWheelZoomModifier. Essentially we want to stop zooming at a certain visibleRange level and we cannot achieve it. Currently we have a heatmap that’s about 1500 pixels in width (with 1500 datapoints, 1-to-1), but the user can zoom so far you essentially see just 1 data point spread over the 1500 pixels.

We want to be able to zoom, but up to a certain limit, let’s say 100 datapoints visible. When visibleRange.max – visibleRange.min <= 100 we want to stop zooming.

We’ve tried suspendUpdates() and manually setting the visibleRange, but this doesn’t produce the desired results because suspendUpdates stops everything (including zooming out again) and resetting visibleRange solves it for a second, but the user is able to zoom in again. We also tried a custom mouseWheelModifier class and just removing the zoom in functionality at the treshold but that does not seem to work because scichart gives an error of having different mouseWheelModifiers.

I hope that this problem was expressed sufficiently. Please let us know if you have any questions.

  • Zino As asked 11 months ago
  • last active 11 months ago
0 votes
3k views

I would like to change the backgound color of my piechart.

const sciChartPieSurface = await SciChartPieSurface.create(props.chartName);
sciChartPieSurface.applyTheme(new SciChartJSLightTheme());

This code above doesnt change anything.

1 vote
2k views

How to achieve the Sci Chat functionality in JavaScript . I want to update the list instead of removing a range of data from the dataseries.

0 votes
4k views

Hi, I noticed that Heattexture: [object Object] is logged hundreds/thousands of times while interacting with a heatmap. This isn’t my console log, as it is a very simple implementation and there are no logs on my front end, with nothing called Heattexture in my repository.

I’m guessing that this is a leftover console log that needs to be removed, as it occurs in both my development and production Webpack builds.

Most of the time it does not cause any performance issues, but once I did see the heatmap flickering at roughly the same rate of the console log.

Screenshot attached.

Thanks!

1 vote
3k views

Hello,

I am attempting to get SciChart working in a Blazor Server app.

I have followed the examples here:
https://github.com/ABTSoftware/SciChart.JS.Examples/tree/master/Sandbox/demo-browser-global-module
https://github.com/ABTSoftware/SciChart.JS.Examples/blob/master/Tutorials/2D_Browser_CDN_Tutorials_JavaScript/Tutorial_2_Adding_Series_and_data/index.html
https://www.scichart.com/documentation/js/current/webframe.html#Deploying%20Wasm%20or%20WebAssembly%20and%20Data%20Files%20with%20your%20app.html
https://cdn.jsdelivr.net/npm/[email protected]/README.md

I have the following line in my “_Layout.cshtml” file in the head section:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/_wasm/scichart.browser.js" crossorigin="anonymous"></script>

In my scichart js I have:

export async function initSciChart() {
    SciChart.SciChartSurface.setRuntimeLicenseKey("....");
    SciChart.SciChartSurface.useWasmFromCDN();
}

However when I call this over JSInterop i get the following error:

Error: Microsoft.JSInterop.JSException: SciChart.SciChartSurface.useWasmFromCDN is not a function
TypeError: SciChart.SciChartSurface.useWasmFromCDN is not a function
at Module.initSciChart (https://localhost:7195/Pages/Strategy/StrategyMain.razor.js:33:30)
at https://localhost:7195/_framework/blazor.server.js:1:3501
at new Promise ()
at kt.beginInvokeJSFromDotNet (https://localhost:7195/_framework/blazor.server.js:1:3475)
at https://localhost:7195/_framework/blazor.server.js:1:72001
at Array.forEach ()
at kt._invokeClientMethod (https://localhost:7195/_framework/blazor.server.js:1:71987)
at kt._processIncomingData (https://localhost:7195/_framework/blazor.server.js:1:70029)
at connection.onreceive (https://localhost:7195/_framework/blazor.server.js:1:64432)
at o.onmessage (https://localhost:7195/_framework/blazor.server.js:1:48766)
at Microsoft.JSInterop.JSRuntime.InvokeAsync[TValue](Int64 targetInstanceId, String identifier, Object[] args)
at Microsoft.JSInterop.JSObjectReferenceExtensions.InvokeVoidAsync(IJSObjectReference jsObjectReference, String identifier, Object[] args)
at TradeLogicBS.Web.Pages.Strategy.StrategyMain.OnAfterRenderAsync(Boolean firstRender) in D:\Repos\TradeLogicBS\TradeLogicBS\TradeLogicBS.Web\Pages\Strategy\StrategyMain.razor:line 63
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)

How do we set this up properly to access the scichart modules in a blazor server application?

Thank you,

  • Leland asked 2 years ago
  • last active 2 years ago
1 vote
2k views

Is it possible, in version 2.2.2404, to have RubberBandXyZoomModifier function like what seems to be the default for v3? So when I’m dragging the box to zoom, if I leave the chart area, the box snaps to the edges of the chart, and upon mouse release, it zooms normally to the box.

I can provide an example:

https://8tunzo.csb.app/ — broken (version 2.2.2404)

https://umvryi.csb.app/ — working (version 3.0.301)

Same exact code

import { SciChartSurface } from "scichart/Charting/Visuals/SciChartSurface";
import { NumericAxis } from "scichart/Charting/Visuals/Axis/NumericAxis";
import { XyDataSeries } from "scichart/Charting/Model/XyDataSeries";
import { FastLineRenderableSeries } from "scichart/Charting/Visuals/RenderableSeries/FastLineRenderableSeries";
import { MouseWheelZoomModifier } from "scichart/Charting/ChartModifiers/MouseWheelZoomModifier";
import { RubberBandXyZoomModifier } from "scichart/Charting/ChartModifiers/RubberBandXyZoomModifier";
import { ZoomExtentsModifier } from "scichart/Charting/ChartModifiers/ZoomExtentsModifier";

const dataX = [0, 10, 20, 30, 40, 50];
const dataY = [0, 10, 5, 20, 15, 35];

// eslint-disable-next-line
SciChartSurface.useWasmFromCDN();

async function initSciChart() {
  const { sciChartSurface, wasmContext } = await SciChartSurface.create(
    "scichart-root"
  );

  const xAxis = new NumericAxis(wasmContext);
  const yAxis = new NumericAxis(wasmContext);
  sciChartSurface.xAxes.add(xAxis);
  sciChartSurface.yAxes.add(yAxis);

  const lineData = new XyDataSeries(wasmContext, {
    xValues: dataX,
    yValues: dataY
  });

  sciChartSurface.renderableSeries.add(
    new FastLineRenderableSeries(wasmContext, { dataSeries: lineData })
  );

  sciChartSurface.chartModifiers.add(new MouseWheelZoomModifier({}));
  sciChartSurface.chartModifiers.add(new RubberBandXyZoomModifier({}));
  sciChartSurface.chartModifiers.add(new ZoomExtentsModifier({}));
}

initSciChart();
1 vote
3k views

Hi There,

I’m new to SciChart, I have a question regarding DateTimeAxis that is it possible to display the date when plotting data based on hours for multiple days?

For example, I’m plotting data for Aug/07, Aug/06, and Aug/05, so I have 72 data points to display, but the problem is the user can see all the data based on hours however, they will not be able to see when the day has changed. Is there any way I can achieve this.

  • Umer Nawaz asked 2 years ago
  • last active 2 years ago
1 vote
6k views

Hi Andrew:

It’s me again.

Does SciChart.js support Vue.js 2.0 or even 3.0 ?

Will there be any Vue templates in the future ?

Best Regards

Yen Wen

0 votes
13k views

paletteProvider transparent colour is not working.

I tried with parseColorToUIntArgb(“#ffffff00”) and parseColorToUIntArgb(“#ffffff00”, 0) to replace the colour with transparent. But its not working.

I want ho hide the marker on the chart if the value is less than a limit. Is any other possibility to hide the marker based on value?
can you check the image, i want to show only the yellow marker.
one more error i am getting from typescript
“Type ‘undefined’ is not assignable to type ‘TPointMarkerArgb'”.

1 vote
2k views

I added the cursor modifier as below:

const chartDefinition = {
    ......,
    modifiers: [
        { 
            type: EChart2DModifierType.Cursor, 
            options: { crosshairStroke: "green",  crosshairStrokeThickness: 1}  
    },
    { type: EChart2DModifierType.MouseWheelZoom },
    { type: EChart2DModifierType.ZoomExtents }
    ]
};

const { sciChartSurface, wasmContext } = await chartBuilder.build2DChart("scichart-root", chartDefinition);  

As I want to show the x, y values of the cursor somewhere outside the chart instead of showing the tooltip. How can I get the x, y values when the cursor moving?

  • Kelly Chan asked 2 years ago
  • last active 2 years ago
1 vote
5k views

I have a live updating chart with multiple traces. After updated SciChart to v3.0.280, I got “Uncaught (in promise) RangeError: Maximum call stack size exceeded” error sometimes when I call XyDataSeries.appendRange(). This error will not be triggered if just initialize the chart and keeps updating the chart data. It seems happening after I modified the visibleRange of x-axis or y-axis. But the error is triggered on the line calling appendRange(). I have no clue for this issue. My codes didn’t change and only updated the SciChart version. Could you find the possible cause of my problem? Please refer to the attached screenshots.

Codes to update the chart data:

    UpdateSuspender.using(sciChartSurfaceRef.current, () => {
        console.time("Time - Update series");
        for (tnum=0; tnum<MAX_TRACE; tnum++) {
            traceObj = tracesInfoObj.current[tnum];
            if (traceSeries.current[tnum] && traceObj.status === "Active") {
                traceSeries.current[tnum]["xyDataSeries"].clear();
                switch (traceObj.type) {
                    case 0:
                        traceSeries.current[tnum]["xyDataSeries"].appendRange(dataX, newSpecData);
                        break;
                    case 1:
                        traceSeries.current[tnum]["xyDataSeries"].appendRange(dataX, newMaxHoldData);
                        break;
                    case 2:
                        traceSeries.current[tnum]["xyDataSeries"].appendRange(dataX, newMinHoldData);
                        break;
                    case 3:
                        traceSeries.current[tnum]["xyDataSeries"].appendRange(dataX, averageData);
                        break;
                }   
            }
        };
        console.timeEnd("Time - Update series");
    });
  • Quyen Sy asked 1 year ago
  • last active 1 year ago
1 vote
2k views

Hello All,

I was trying to set the visual range property on a numeric axis but i get the error that the “equals” is not a function of NumerixAxis. This happens when i try the following.

export function setXAxisVisibleRange(element, visibleRange) {
     const { sciChartSurface } = resolveContext(element);
     var axis = sciChartSurface.xAxes.get(0);
     axis.visibleRange = visibleRange; **//error here**
}

attached is a picture of a more detailed error. Ultimately what I am trying to do is to sync up the visual range of two charts x axis like in the following tutorial

https://www.scichart.com/documentation/js/current/webframe.html#Tutorial%2009%20-%20Linking%20Multiple%20Charts.html

Thanks for your help in advance.

UPDATE

So my issue was that I had a serialization issue with the NumberRange class. I can now sync up the charts just like in the tutorial. The problem now is that if I scroll or zoom very fast eventually the charts start to flicker in an endless loop. Has anyone ever seen this behavior? One thing I notice is that the min and max value have over 10 decimal points.

Here is a quick printout of the value change events between the two charts when the flickering starts.

Any ideas?

–> CH.1 Min: 0.532180460735786 Max: 3.457511660735786
–> CH.2 Min: -0.17488026755852848 Max: 4.339519732441472
–> CH.2 Min: 0.025927491638795958 Max: 4.088887491638796
–> CH.2 Min: 0.38738145819397996 Max: 3.63774945819398
–> CH.1 Min: 0.5319630448160535 Max: 3.4572942448160537
–> CH.2 Min: 0.532180460735786 Max: 3.457511660735786
–> CH.1 Min: -0.17488026755852848 Max: 4.339519732441472
–> CH.1 Min: 0.025927491638795958 Max: 4.088887491638796
–> CH.1 Min: 0.38738145819397996 Max: 3.63774945819398
–> CH.2 Min: 0.5319630448160535 Max: 3.4572942448160537
–> CH.1 Min: 0.532180460735786 Max: 3.457511660735786
–> CH.2 Min: -0.17488026755852848 Max: 4.339519732441472
–> CH.2 Min: 0.025927491638795958 Max: 4.088887491638796
–> CH.2 Min: 0.38738145819397996 Max: 3.63774945819398
–> CH.1 Min: 0.5319630448160535 Max: 3.4572942448160537
–> CH.2 Min: 0.532180460735786 Max: 3.457511660735786
–> CH.1 Min: -0.17488026755852848 Max: 4.339519732441472
–> CH.1 Min: 0.025927491638795958 Max: 4.088887491638796
–> CH.1 Min: 0.38738145819397996 Max: 3.63774945819398
–> CH.2 Min: 0.5319630448160535 Max: 3.4572942448160537
–> CH.1 Min: 0.532180460735786 Max: 3.457511660735786
–> CH.2 Min: -0.17488026755852848 Max: 4.339519732441472
–> CH.2 Min: 0.025927491638795958 Max: 4.088887491638796
–> CH.2 Min: 0.38738145819397996 Max: 3.63774945819398
–> CH.1 Min: 0.5319630448160535 Max: 3.4572942448160537
–> CH.2 Min: 0.532180460735786 Max: 3.457511660735786
–> CH.1 Min: -0.17488026755852848 Max: 4.339519732441472
–> CH.1 Min: 0.025927491638795958 Max: 4.088887491638796
–> CH.1 Min: 0.38738145819397996 Max: 3.63774945819398
–> CH.2 Min: 0.5319630448160535 Max: 3.4572942448160537
–> CH.1 Min: 0.532180460735786 Max: 3.457511660735786
–> CH.2 Min: -0.17488026755852848 Max: 4.339519732441472

0 votes
7k views

Hi,
sciChartSurface.chartModifiers.clear() is getting error on the application. Its working on previous version and getting error on 1.4.1557.

1 vote
6k views
  1. SciChartSurface created with category x and numeric y axes
  2. ZoomPanModifier, ZoomExtentsModifier, MouseWheelZoomModifier, RolloverModifier added.
  3. Any series and dataSeries are initialized and added to the surface, but not filled with values yet.
  4. Value streaming started with delay.

The first error occurs when trying to zoom on empty surface :

Uncaught TypeError: Cannot read properties of undefined (reading 'width')
at MouseWheelZoomModifier2.ChartModifierBase2D2.growBy (ChartModifierBase2D.js:121:48)

And the other one when trying to drag:

Uncaught Error: category x axis with id=DefaultAxisId should have data series count > 0
at CategoryAxis2.CategoryAxisBase2.getCurrentCoordinateCalculatorInternal (CategoryAxisBase.js:95:19)

I have seen errors similar to the second one in other scenarios and sometimes it is fatal.
E.g i have tried to add annotation right after first value was added and first line raised similar error:

    const xCalc = this.surface.xAxes.get(0).getCurrentCoordinateCalculator() as CategoryCoordinateCalculator;
    const realX = xCalc.transformDataToIndex(time);
    const target = this.getOrAddAnnotation();
    target.x1 = realX;
    target.y1 = value;

Also getCurrentCoordinateCalculatorInternal error occurs when empty series & seriesData added without suspend.
I would appreciate any help and clarification

1 vote
4k views

I have a real time updated chart and users can add annotation to the chart. The position of the annotation will be updated with the chart data. I would like to stop updating the annotation position while user dragging the annotation. So I added a flag (e.g. isDragging) to the annotation dragStarted and dragEnded events. The isDragging flag will be set to true in dragStarted and then set to false in dragEnded. I will check this flag before updating the annotation position.

Here’s the problem, the annotation dragStarted event will be triggered when users do panning in the chart. But the dragEnded will not be triggered in this case. It breaks my plan to stop updating the annotation position as the isDragging will be incorrect. Is it a bug that the annotation dragStarted event (but not the dragEnded event) triggered when panning?

  • Quyen Sy asked 1 year ago
  • last active 1 year ago
1 vote
966 views

Hello,

I switched from NumericAxis to DateTimeNumericAxis to view x axis labels in hh:mm format. It is definitely showing the labels in this format which is what I wanted, but for some reason it always show “Jan 1” in the first label. I simply want to show hh:mm labels based on the X values. One thing to now is that this is a live updating chart, not static data.

What am I missing? attached is a picture showing the issue and a picture with the DateTimeNumericAxis configuration.

Thanks.
Sergio.

0 votes
4k views

Hi there,

I would like to use a column chart with labels. Our client tends to use really long names for these labels, so I was wondering if it is possible that these labels wrap. I could find no property in the docs or source that achieves this. The attached screenshot shows the current (and undesired) rendering.

The current behavior seems to be that labels in between are hidden when longer labels overlap them. Is this a configurable property? In our case, the labels may never be hidden.

Kind regards,
Tim Havinga

1 vote
3k views

I have a simple need to display a horizontal heatmap based on values along the x-axis. So if value on x-axis is 1, color shown on the heatmap should be orange, if the next value is 2, red color bar appears to next the previous orange etc. Something like this (see attached graphic also):

Heatmap: [=orange=red=green]
values along x-axis: [22.05, 24.00, 30.00 ]

So the code I am trying is as follows:

export async function renderHeatMap(element) 
{
    const { sciChartSurface, wasmContext } = await SciChartSurface.create(element);

    sciChartSurface.xAxes.add(new NumericAxis(wasmContext));
    sciChartSurface.yAxes.add(new NumericAxis(wasmContext));

    var heatMapData = zeroArray2D([1, 5]);
    heatMapData[0][0] = 22.05; //should appear as color1 in heatmap
    heatMapData[0][1] = 24.00; //...
    heatMapData[0][2] = 30.00; //should appear as color2 in heatmap
    heatMapData[0][3] = 26.75;
    heatMapData[0][4] = 30.00; //should appear as color3 in heatmap

    const heatmapDataSeries = new UniformHeatmapDataSeries(wasmContext, 0, 1, 0, 1, heatMapData);

    const heatmapSeries = new UniformHeatmapRenderableSeries(wasmContext, {
        dataSeries: heatmapDataSeries,
        colorMap: new HeatmapColorMap({
            minimum: 20, // min value in the zValues (data) to map to offset 0 in the colormap
            maximum: 30, // max value in the zValues (data) to map to offset 1 in the colormap
            gradientStops: [
                { offset: 0, color: "#00008B" },
                { offset: 0.3, color: "#7FFF00" },
                { offset: 0.7, color: "#FFFF00" },
                { offset: 1.0, color: "#FF0000" },
            ],
        }),
    });

    sciChartSurface.renderableSeries.add(heatmapSeries);
}

But this only displays an empty grid. I don’t see a heatmap. Anyone can point out what may be wrong? Not even the axis is showing up correctly.

1 vote
3k views

I am considering applying server-side licensing for my javerScript application.

In the document below, there is a phrase “Our server-side licensing component is written in C++.”
(https://support.scichart.com/support/solutions/articles/101000515345-scichart-js-domain-licensing-faq)

However, there is only asp.net sample code on the provided github.
(https://github.com/ABTSoftware/SciChart.JS.Examples/tree/master/Sandbox/demo-dotnet-server-licensing)

I wonder if there is a sample code implemented in C++ for server-side licensing.

Can you provide c++ sample code?
Also, are there any examples to run on Ubuntu?

0 votes
3k views

Hi,

we have recently bought Bundle licenses to be able to use SciChart JS. We are non-profit and make in-house web-applications.
I want to migrate to it asap but need urgent clarification on the licensing. There is only 1 domain allowed per developer. This seems strange to me because we run multiple micro-UIs under different domains. We also have dev & test environments. Do we need an extra license for all of these?

Please clarify. We are very happy with SciChart but this would be a major problem for us.

1 vote
10k views

Do scichart supports microsoft blazor ?
If yes, Any examples available ?

  • Abhilash R asked 3 years ago
  • last active 2 years ago
0 votes
6k views

I would like to create an arc annotation as shown in the attached image in Scichart JavaScript.
Please guide me on what will be the best way.

1 vote
2k views

I am using SciChartSurface.create() to create chart and it works well. However, I got the webGL memory issue randomly – WebGL memory issue. I am trying to apply a workaround to use SciChartSurface.createSinge() instead. The createSingle() function doesn’t work for me, the chart cannot be rendered.

const { sciChartSurface, wasmContext } = await SciChartSurface.createSinlge(`${chartId}-chart-root`, { theme: themeObj });

There is no error showing in the developer tool console. I have no idea what’s wrong.

  • Quyen Sy asked 6 months ago
  • last active 6 months ago
0 votes
4k views

Screenshots attached.

To recreate:

  • Create a chart
  • Add a CursorModifier – default options is fine
  • Either directly remove the modifier using .remove(modifier), or clear all using .clear()

Expected behaviour:
– Should remove the cursor modifier

Actual behaviour with bug:
– Crashes the page

This appears to be an issue with CursorModifier only (out of the 5 or 6 that I’ve tried). Other modifiers work as expected.

Traced the issue to this.parentSurface being undefined in CursorModifier.js, so when onDetach() is called, it errors.

Thanks!
Joe

1 vote
2k views

I have a top chart that is a heatmap and there is another bottom chart that is a temperature map. I want the heatmap not show any y-axis labels but still keep the x-axis start aligned from the left side. I tried turning off the y-axis lables (which I do not need in the heatmap), but that moves the heatmap to the left and not kept aligned with the bottom chart.

const yAxis = new NumericAxis(wasmContext, {
autoRange: EAutoRange.Always,
labelFormat: ENumericFormat.Decimal,
labelPrecision: 2,
labelPostfix: "C",
drawLabels: false
});

The above code hides the y-axis labels but also removes the space on the left side so the x-axis of both graphs does not remained aligned. I need a way to somehow keep the start of x-axis aligned for both top and bottom graphs.

1 vote
1k views

I am considering applying server-side licensing for my javerScript application.

In the document below, there is a phrase “Our server-side licensing component is written in C++.”
(https://support.scichart.com/index.php?/Knowledgebase/Article/View/17256/42/)

However, there is only asp.net sample code on the provided github.
(https://github.com/ABTSoftware/SciChart.JS.Examples/tree/master/Sandbox/demo-dotnet-server-licensing)

I wonder if there is a sample code implemented in C++ for server-side licensing.

Can you provide c++ sample code?
Also, are there any examples to run on Ubuntu?

0 votes
84 views

Hello,

I am having a problem where I want to display the Rollover Modifier only when I click on the selected graph.
For example:
https://demo.scichart.com/javascript-chart-rollovermodifier-tooltips
If I select the Second Lines Series, I want to see the rollover only for that particular graph and not the first one and the third one.

I have tried using charts id’s as a reference to show only one tooltip but it just enables it for all the graphs.

Any help would be kindly appreciated.

  • Zino As asked 2 weeks ago
  • last active 2 weeks ago
1 vote
6k views

Hello,

I am currently developing an application using Electron (v. 8.3.0), React (v. 17.0.1), and TypeScript (v. 4.0.5). I have created a simple demo app (see attached) to test this software, and I found that whenever the application window is resized, there is an “Uncaught null” error from scichart2d.js caught by the console. I also found that at some window dimensions, it even seems that the chart cannot properly be displayed and it will try to constantly rerender, quickly filling up the console with the same error.

A couple of notes:

  • I currently am using a trial key.
  • I am compiling this application using electron-webpack (https://webpack.electron.build/). It has a few built-in tools that ease the compilation/app creation process when combining Electron with other frameworks. This means some of the webpack API being used is abstracted, but we can append our own (see webpack.renderer.additions.js), more information can also be found at https://webpack.electron.build/modifying-webpack-configurations.
  • Electron v. 8.3.0 uses Chromium 80, so all features should be supported.
0 votes
3k views

can i use scichart in vue2.0 or 3.0?

  • YEJI YOON asked 3 years ago
  • last active 3 years ago
0 votes
3k views

Hello,

I have a Blazor WASM app running Scichart JS. In attempting to get any of the filters working listed in the Filters API docs I am unable to build my application and get thrown an error.

As listed in Moving Average Filter Docs I import the XyMovingAverageFilter:

import { XyMovingAverageFilter } from ‘scichart/Charting/Model/Filters/XyMovingAverageFilter’;

Run npm install and build the application and I get the error:
Module not found: Error: Can’t resolve ‘scichart/Charting/Model/Filters/XyMovingAverageFilter’ in …

So far I have not run into any issues getting any of the Scichart JS APIs working in this manner.

  • Leland asked 2 years ago
  • last active 2 years ago
1 vote
2k views

I’m trying to add an AxisMarkerAnnotations for some line series on my chart.

I was following these questions answered here:
https://www.scichart.com/questions/js/adding-axis-markers-to-fastlinerenderableseries
https://www.scichart.com/questions/js/price-indicators-on-yaxis

I managed to render the axis marker for my series. Now I’m trying to the set the Y value of the marker annotation as the value where the line intersects the Y Axis.
I want the marker value to be the last value of the series, only if the xAxis.visibleRange.max > series.XdataRange.max.

The two attached images show the desired feature.
One shows the marker annotation with the Y1 being the one where the line intersects the YAxis.
The other shows the marker annotation with the Y1 being the last value of the line, because it is visible.

I read some answers here on the forum about using the HitTest API to do this, but I couldn’t make it work on my JS app

0 votes
13k views

I see in the documentation that I need to set the boolean to true/false. But my question is how do I do this after the series has been rendered? I am using a custom legend and need to be able to toggle visibility.

  export default function Chart() {
  const [sciChartSurface, setSciChartSurface] = React.useState<SciChartSurface>();
  const [wasmContext, setWasmContext] = React.useState<TSciChart>();

  const dataSeriesMapRef = React.useRef<Map<keyof typeof chartData, XyDataSeries>>();

  React.useEffect(() => {
    (async () => {
      const { sciChartSurface, wasmContext } = await drawChart(theme, chartType);
      setSciChartSurface(sciChartSurface);
      setWasmContext(wasmContext);
    })();

    dataSeriesMapRef.current = new Map<keyof typeof chartData, XyDataSeries>();

    return () => sciChartSurface?.delete();
  }, [chartType]); // make sure the chart is initialized only once

  React.useEffect(() => {
    if (dataSeriesMapRef.current && sciChartSurface && wasmContext && !isLoading) {
      // const currentSeries = sciChartSurface.renderableSeries.asArray();
      // if (currentSeries) sciChartSurface.renderableSeries.clear();
      updateChartWithData(dataSeriesMapRef.current, theme, wasmContext, sciChartSurface, chartData, chartType);
    }
  }, [theme, chartData, wasmContext, sciChartSurface, isLoading]);

  return (
    <div id="chart-container">
      <ControlsLegend
        chartData={chartData}
        dataSeriesMap={dataSeriesMapRef.current as Map<keyof typeof chartData, XyDataSeries>}
        theme={theme}
        sciChartSurface={sciChartSurface as SciChartSurface}
        wasmContext={wasmContext as TSciChart}
      />
      <div id={DIVID} style={{ width: windowWidth, height: windowHeight }} />
      <ControlsBottom />
    </div>
  );
}

drawChart.js

    export default async (theme: ExtendedTheme, chartType: string) => {
  const { sciChartSurface, wasmContext } = await SciChartSurface.create(DIVID);
  const isLightTheme = theme.palette.type === 'light';

  console.log('createChart');
  const xAxis = new NumericAxis(wasmContext);
  const yAxis = new NumericAxis(wasmContext);

  xAxis.labelProvider.formatLabel = (unixTimestamp: number) => {
    return new Date(unixTimestamp * 1000).toLocaleDateString('en-us', {
      month: 'short',
      year: 'numeric',
      day: 'numeric',
    });
  };

  yAxis.labelProvider = new CustomLabelProvider();

  sciChartSurface.yAxes.add(yAxis);
  sciChartSurface.xAxes.add(xAxis);

  if (chartType !== 'stack') sciChartSurface.chartModifiers.add(new RolloverModifier({ showRolloverLine: false }));
  sciChartSurface.chartModifiers.add(new ZoomPanModifier());
  sciChartSurface.chartModifiers.add(new ZoomExtentsModifier());
  sciChartSurface.chartModifiers.add(new MouseWheelZoomModifier());
  sciChartSurface.chartModifiers.add(new PinchZoomModifier());

  const rubberBandXyZoomModifier = new RubberBandXyZoomModifier({
    isAnimated: true,
    animationDuration: 400,
    easingFunction: easing.outExpo,
    fill: '#FFFFFF33',
    stroke: '#FFFFFF77',
    strokeThickness: 1,
  });
  sciChartSurface.chartModifiers.add(rubberBandXyZoomModifier);

  sciChartSurface.zoomExtents();

  if (isLightTheme) sciChartSurface.applyTheme(new SciChartJSLightTheme());
  else sciChartSurface.applyTheme(new SciChartJSDarkTheme());
  return { sciChartSurface, wasmContext };
};

updateChartWithData.js

    export const updateChartWithData = (
  dataSeriesMap: Map<keyof typeof chartData, XyDataSeries>,
  theme: ExtendedTheme,
  wasmContext: TSciChart,
  sciChartSurface: SciChartSurface,
  chartData: ChartData,
  chartType: string
) => {
  sciChartSurface.invalidateElement();
  const streamIds = Object.keys(chartData);
  const isLightTheme = theme.palette.type === 'light';

  if (isLightTheme) sciChartSurface.applyTheme(new SciChartJSLightTheme());
  else sciChartSurface.applyTheme(new SciChartJSDarkTheme());

  sciChartSurface.invalidateElement();
  console.log('updateChartWithData');
  console.log(chartData);

  const stackedColumns: any[] = [];
  streamIds.forEach((s) => {
    const streamId = Number(s) as keyof ChartData;
    const streamSelectorId = streamId as keyof typeof chartData;
    if (dataSeriesMap.has(streamSelectorId)) {
      const xyDataSeries = dataSeriesMap.get(streamSelectorId);
      chartData[streamId].data.forEach((value) => {
        xyDataSeries?.append(value.timestamp, value.value);
      });
    } else if (chartType === 'scatter') {
      const lineSeries = LineChart(dataSeriesMap, theme, wasmContext, chartData, streamSelectorId, streamId);

      sciChartSurface.renderableSeries.add(lineSeries);
      sciChartSurface.zoomExtents();
    } else if (chartType === 'bar') {
      const lineSeries = BarChart(dataSeriesMap, theme, wasmContext, chartData, streamSelectorId, streamId);
      sciChartSurface.renderableSeries.add(lineSeries);
      sciChartSurface.zoomExtents();
    } else {
      const lineSeries = StackedChart(dataSeriesMap, theme, wasmContext, chartData, streamSelectorId, streamId);
      stackedColumns.push(lineSeries);
    }
  });
};

“LineChart.js”

    export default (
  dataSeriesMap: Map<keyof typeof chartData, XyDataSeries>,
  theme: ExtendedTheme,
  wasmContext: TSciChart,
  chartData: ChartData,
  streamSelectorId: keyof typeof chartData,
  streamId: keyof ChartData
) => {
  const xyDataSeries = new XyDataSeries(wasmContext);
  dataSeriesMap.set(streamSelectorId, xyDataSeries);

  const obj = chartData[streamSelectorId];
  const stroke = obj.stroke as string;
  xyDataSeries.dataSeriesName = obj.label as string;

  chartData[streamId].data.forEach((value) => {
    xyDataSeries.append(value.timestamp, value.value);
  });

  const lineSeries = new FastLineRenderableSeries(wasmContext, {
    stroke,
    strokeThickness: obj.strokeThickness,
    strokeDashArray: obj.strokeDashArray,
    dataSeries: xyDataSeries,
    animation: new WaveAnimation({ zeroLine: -1, pointDurationFraction: 0.5, duration: 1000 }),
  });

  lineSeries.rolloverModifierProps.tooltipColor = theme.palette.background.paper;
  lineSeries.rolloverModifierProps.tooltipTextColor = theme.palette.getContrastText(stroke);
  lineSeries.rolloverModifierProps.markerColor = stroke;
  lineSeries.rolloverModifierProps.tooltipTemplate = (
    id: string,
    tooltipProps: RolloverModifierRenderableSeriesProps,
    seriesInfo: SeriesInfo,
    updateSize: (width: number, height: number) => void
  ) => {
    const { tooltipColor, tooltipTextColor, markerColor } = tooltipProps;
    const { formattedXValue, yValue, seriesName } = seriesInfo;
    const width = 192;
    const height = 60;

    updateSize(width, height);
    return `<svg width='${width}' height='${height}' class="root">
        <rect width="100%" height="100%" fill='${tooltipColor}' stroke='${markerColor}' stroke-width='2' />
         <svg width='100%'>
           <text dy="0" fill='${tooltipTextColor}'>
            <tspan x="15" y="20" class="title">${formattedXValue}</tspan>
             <tspan x="15" y="45" class="value">
              ${seriesName} | ${yValue.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
             </tspan>
          </text>
        </svg>
      </svg>`;
  };
  return lineSeries;
};

lengend.js

    let _chartData: any[] = [];
export default ({
  dataSeriesMap,
  theme,
  wasmContext,
  sciChartSurface,
  chartData,
}: {
  dataSeriesMap: Map<keyof typeof chartData, XyDataSeries>;
  theme: ExtendedTheme;
  wasmContext: TSciChart;
  sciChartSurface: SciChartSurface;
  chartData: ChartData;
}) => {
  const classes = useStyles();

  const chartRef = React.useRef<any>([]);
  const [legendData, setLegendData] = React.useState<any[]>([]);

  // _chartData = [..._chartData, ...values(chartData)];
  _chartData = values(chartData);
  _chartData = uniqby(_chartData, 'groupId');
  _chartData = uniqby(_chartData, 'streamId');

  React.useEffect(() => {
    if (_chartData.length > 0 && !isEqual(chartRef.current, _chartData)) {
      setLegendData(_chartData);
      chartRef.current = _chartData;
    }
  }, [_chartData]);

  const onClick = React.useCallback(
    (item) => {
      // const xyDataSeries = dataSeriesMap.get(160);
      const _legendData = legendData.map((x) => {
        if (x.groupId === item.groupId) x.isVisible = !x.isVisible;
        return x;
      });

      setLegendData(_legendData);
    },
    [legendData]
  );

  return (
    <div className={classes.root}>
      <Paper variant="outlined" square>
        {legendData.map((item: any) => (
          <div className={classes.listItem} key={item.streamId}>
            <IconButton
              onClick={() => onClick(item)}
              style={{ color: item.stroke }}
              size="small"
              aria-label={item.label}
            >
              {item.isVisible ? <VisibilityIcon /> : <VisibilityOffIcon />}
            </IconButton>
            <div>{item.label}</div>
          </div>
        ))}
      </Paper>
    </div>
  );
};

Inside the onClick function is where I was trying to change visibility for both series. I’ve tried multiple things without any luck. I assumed it would be as simple as getting the correct series, setting the visible property and maybe a re-render if that is even needed? Like I said any advice on a better way of handling this is welcome

0 votes
4k views

I am having UX issues with overlapping Bars on BarChart with certain datasets. Example attached is the code
https://codesandbox.io/p/sandbox/javascript-column-chart-forked-p59p75?file=%2Fsrc%2FApp.tsx%3A49%2C1

please uncomment lines at 47 and 48 for a different set of x and y values where it works fine.
Not sure what am i doing wrong.

1 vote
4k views

Hi,

We use SciChart extensively in our WPF applications and make use of CustomRenderableSeries, writing all kinds of different lines and bitmaps directly to the IRenderContext2D depending on the point metadata.

We’re now looking at doing something similar in JavaScript, but can’t find any examples of this kind of customisation in the documentation. In the JavaScript library, is it possible to draw directly to a ‘canvas’ in the same way as in the WPF version? If so, could you please point to an example?

Many thanks,

Graham.

1 vote
2k views

Hello,

I want to render a base64 image in ScichartJS without actually rendering it in DOM. I saw that you have these guides in WPF:
https://support.scichart.com/index.php?/Knowledgebase/Article/View/17213/34/
https://www.scichart.com/documentation/win/current/webframe.html#Screenshots,_Printing_and_Export_to_XPS.html

However it is difficult to achieve it in Typescript. Is there a guide or any ideas on how to do it? I would appreciate any answer.

  • Zino As asked 9 months ago
  • last active 9 months ago
1 vote
5k views

Hello,

Can you show this example(https://codepen.io/PodaRocheK/pen/yLGxGEL?editors=0011) and tell me, why metadata labels are not displayed in some cases? May be you can tell me how to do it correctly? A have similar chart with a lot of datapoints and can’t understand why labels show only in some time(20s or > 1m after start the chart)

Thank you!

0 votes
4k views

Hello, I am running scichart in a blazorwasm app. On updating from 1.x to 2.2.2351 I am getting an uncaught error for a missing module on app startup before I make any calls to scichart to initialize.

The error is:
Uncaught Error: Cannot find module ‘../Charting/Model/Filters/HlcScaleOffsetFilter’
at webpackMissingModule (buildDataSeries.js:15:94)
at eval (buildDataSeries.js:15:215)
at Object../node_modules/scichart/Builder/buildDataSeries.js (strategyChart.js:133:1)
at webpack_require (strategyChart.js:21:30)
at eval (chartBuilder.js:45:25)
at Object../node_modules/scichart/Builder/chartBuilder.js (strategyChart.js:181:1)
at webpack_require (strategyChart.js:21:30)
at eval (SciChartSurface.js:30:22)
at Object../node_modules/scichart/Charting/Visuals/SciChartSurface.js (strategyChart.js:3253:1)
at webpack_require (strategyChart.js:21:30)

My webpack is:

 const path = require("path");
const CopyPlugin = require("copy-webpack-plugin");
const webpack = require("webpack");
module.exports = {
    mode: "development",
    entry: {
        strategyChart: './src/strategyChart.js'
    },
    module: {
        rules: []
    },
    resolve: {
        extensions: [".js"]
    },
    output: {
        path: path.resolve(__dirname, '../wwwroot'),
        filename: "[name].js",
        library: "[name]"
    },
    plugins: [
        new CopyPlugin({
            patterns: [
                { from: "node_modules/scichart/_wasm/scichart2d.data", to: "" },
                { from: "node_modules/scichart/_wasm/scichart2d.wasm", to: "" }
            ]
        }),
        new webpack.IgnorePlugin(/(fs)/)
    ]
};
  • Leland asked 2 years ago
  • last active 2 years ago
1 vote
4k views

I recently upgraded SciChart from version 2 to version 3.0.266. It was working fine. But I got error (please refer to the attached screenshot) when I try to run my application today. It’s so strange as it worked fine yesterday and I didn’t make any codes change today.

  • Quyen Sy asked 1 year ago
  • last active 1 year ago
0 votes
2k views

I am trying to create a live updated non-uniform heatmap with uniform xStep but non-uniform yStep. But I got color mapping problem when I tried to create the non-uniform heatmap. The color showing in the chart doesn’t map with the ColorMap value. Below are my codes:

Draw the non-uniform heatmap:

const SPECTROGRAM_HEIGHT = 256;
    const SPECTROGRAM_WIDTH = 100;

    const { sciChartSurface, wasmContext } = await SciChartSurface.create("spectrogram-chart-root");

    const xAxis = new NumericAxis(wasmContext, {
        axisTitle: "Frequency",
        axisTitleStyle: {
            fontSize: CHART_STYLE.AXIS_FONT_SIZE,
            fontFamily: "sans-serif",
            fontWeight: "bold"
        },
        labelStyle: {
            fontSize: CHART_STYLE.LABEL_FONT_SIZE,
            fontFamily: "sans-serif"
        },
        labelFormat: ENumericFormat.Decimal,
        labelPrecision: 6,
        cursorLabelFormat: ENumericFormat.Decimal,
        cursorLabelPrecision: 6,
        drawMajorBands: false,
    });

    const yAxis = new NumericAxis(wasmContext, {
        axisTitle: "Time",
        axisTitleStyle: {
            fontSize: CHART_STYLE.AXIS_FONT_SIZE,
            fontFamily: "sans-serif",
            fontWeight: "bold"
        },
        labelStyle: {
            fontSize: CHART_STYLE.LABEL_FONT_SIZE,
            fontFamily: "sans-serif"
        },
        drawMajorBands: false,
    });

    // Add XAxis and YAxis
    sciChartSurface.xAxes.add(xAxis);
    sciChartSurface.yAxes.add(yAxis);

    const colorMap = new HeatmapColorMap({
        minimum: -200,
        maximum: -50,
        gradientStops: [
            { offset: 1, color: COLORS.DARK_RED },
            { offset: 0.8, color: COLORS.RED },
            { offset: 0.6, color: COLORS.YELLOW },
            { offset: 0.5, color: COLORS.GREEN },
            { offset: 0.4, color: COLORS.BLUE },
            { offset: 0.01, color: COLORS.DARK_BLUE },
            { offset: 0, color: "Transparent" },
        ]
    });

    // Create a Heatmap Data-series. Pass heatValues as a number[][] to the UniformHeatmapDataSeries
    zValues = Array.from(Array(SPECTROGRAM_HEIGHT), () => Array(SPECTROGRAM_WIDTH).fill(-200));

    const heatmapSeries = new NonUniformHeatmapRenderableSeries(wasmContext, {
        dataSeries: new NonUniformHeatmapDataSeries(wasmContext, { zValues: zValues, xCellOffsets: getHeatmapXOffset, yCellOffsets: getHeatmapYOffset }),
        colorMap: colorMap,
        useLinearTextureFiltering: true,
        fillValuesOutOfRange: true,
    });

    // Add heatmap to the chart
    sciChartSurface.renderableSeries.add(heatmapSeries);

I simply return the index for testing in the getHeatmapXOffset and getHeatmapYOffset functions:

const getHeatmapXOffset = (index) => {
    return index;
};

const getHeatmapYOffset = (index) => {
    return index;
};

I fill the zValues array with the minimum value -200, but the data displayed in the chart is COLORS.YELLOW. I don’t understand what’s wrong.

  • Quyen Sy asked 12 months ago
  • last active 12 months ago
1 vote
7k views

Working on an application that requires toggling between Linear and Logarithmic scales. I am using the JavaScript version of the library, and I see that there is a LogarithmicNumericAxis available in the WPF version but there is not one currently supported in the JS version.

Curious if there were any recommendations on how to go about hand rolling this type of functionality?

The application is doing realtime streaming of data points so need to be able to update existing values as well as handle incoming values.

0 votes
3k views

We found that when the entered data is wrong and SciChart exception occurs, we can’t get back to normal without F5 reloading.

In our application, when switching the symbol/screen, it will start over from SciChartSurface.create(), but it just cannot return to normal.

If the abnormal data causes scichart exception, what can we do to minimize the damage ?

Here is demo video :
https://youtu.be/N066BHB83Ik

Showing 101 - 150 of 350 results