Mapbox dash line animation - animation

I am exploring ways to illustrate a directional walking route via animating dots (that are all smoothly moving in 1 direction) and I wanted to see if there is anything that I am missing. I am running into a similar issue in "#react-native-mapbox-gl/maps": "8.5.0" with MapboxGL.LineLayer and then updating style.lineDasharray via setInterval to simulate an animation. The issue also seem to persist in mapbox-gl-js/v0.34.
It appears that the animation looks correct when using lineCap: 'butt', but it breaks down when I switch to lineCap: 'round'. Even when it works correctly, you can see a 1 pixel static lines with line-cap: "butt". I assume it's it looks broken when we switch to "round" because the library code attempts to create a round shape and adds left & right pieces. Basically, it looks like some dashes are moving and some are static (see attached videos). Any tricks to make this into a smooth mono-directional animation? Here is a code snippet that demonstrates the issue.
https://codepen.io/prototypersf/pen/XWYwBYN?editors=1010
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.34.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.34.0/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibHVjYXN3b2oiLCJhIjoiY2l5Nmg4cWU1MDA0ejMzcDJtNHJmZzJkcyJ9.WhcEdTYQH6sSw2pm0RSP9Q';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-122.486052, 37.830348],
zoom: 15
});
map.on('load', function () {
map.addLayer({
"id": "route",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-122.48369693756104, 37.83381888486939],
[-122.48348236083984, 37.83317489144141],
[-122.48339653015138, 37.83270036637107],
[-122.48356819152832, 37.832056363179625],
[-122.48404026031496, 37.83114119107971],
[-122.48404026031496, 37.83049717427869],
[-122.48348236083984, 37.829920943955045],
[-122.48356819152832, 37.82954808664175],
[-122.48507022857666, 37.82944639795659],
[-122.48610019683838, 37.82880236636284],
[-122.48695850372314, 37.82931081282506],
[-122.48700141906738, 37.83080223556934],
[-122.48751640319824, 37.83168351665737],
[-122.48803138732912, 37.832158048267786],
[-122.48888969421387, 37.83297152392784],
[-122.48987674713133, 37.83263257682617],
]
}
}
},
"layout": {
//"line-join": "round",
"line-cap": "round"
},
"paint": {
"line-color": "#888",
"line-width": 6
}
});
var dashLength = 1;
var gapLength = 13;
// We divide the animation up into 40 steps to make careful use of the finite space in LineAtlas //
var steps = 24;
// A # of steps proportional to the dashLength are devoted to manipulating the dash
var dashSteps = steps * dashLength / (gapLength + dashLength);
// A # of steps proportional to the gapLength are devoted to manipulating the gap
var gapSteps = steps - dashSteps;
// The current step #
var step = 0;
setInterval(function() {
step = step + 1;
if (step >= steps) step = 0;
var t, a, b, c, d;
if (step < dashSteps) {
t = step / dashSteps;
a = (1 - t) * dashLength;
b = gapLength;
c = t * dashLength;
d = 0;
} else {
t = (step - dashSteps) / (gapSteps);
a = 0;
b = (1 - t) * gapLength;
c = dashLength;
d = t * gapLength;
}
map.setPaintProperty("route", "line-dasharray", [a, b, c, d]);
}, 125);
map.setPaintProperty("route", "line-dasharray", [1, 2, 1, 4]);
});
</script>
</body>
</html>

Related

svg.js, click event does not fire when animation is running

I try to bind a click event on symbol in a svg.
When a element spinns in the symbol, the click event is not fired, stops/pauses the animation, the click event fires.
How can i fix this, that the click events get fired every time i click on it, regardless if the animations run or not.
.as-console-wrapper{
display: none!important;
}
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Schematic</title>
<script src="https://cdn.jsdelivr.net/npm/#svgdotjs/svg.js#3.0/dist/svg.min.js"></script>
<!--<script src="assets/js/vue.min.js"></script>-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.12/vue.common.dev.min.js"></script>
<script>
const color = {
blue: "#007bff",
indigo: "#6610f2",
purple: "#6f42c1",
pink: "#e83e8c",
red: "#dc3545",
orange: "#fd7e14",
yellow: "#ffc107",
green: "#28a745",
teal: "#20c997",
cyan: "#17a2b8",
white: "#fff",
gray: "#6c757d",
"gray-dark": "#343a40",
primary: "#007bff",
secondary: "#6c757d",
success: "#28a745",
info: "#17a2b8",
warning: "#ffc107",
danger: "#dc3545",
light: "#f8f9fa",
dark: "#343a40"
};
let baseColor = {
background: "#000",
"fill-opacity": 0.5,
stroke: color.white,
"stroke-width": 1,
"stroke-opacity": 0.5
};
let pipeColor = {
fill: color.blue,
opacity: 1
};
let pumpColor = {
fill: color.gray,
"fill-opacity": 0.8
}
document.addEventListener("DOMContentLoaded", () => {
// parent drawing
let draw = SVG().addTo('#schematic').size("100%", 500);
let pumpGroup = draw.symbol();
pumpGroup.click(function () {
alert("Pump clicked!");
});
let height = 50;
let radius = 30;
let chase = pumpGroup.rect(80, height).attr(baseColor).move(0, 0); // 520, 370
let motor1 = pumpGroup.circle(radius).attr(pumpColor).move(45, (height / 2) - radius / 2); // 525, 380
let motor2 = pumpGroup.circle(radius).attr(pumpColor).move(5, (height / 2) - radius / 2); // 565, 380
let fan1 = pumpGroup.image("https://cdn0.iconfinder.com/data/icons/screw/512/fan-ventilator-propeller-rotor-motion-512.png").scale(0.05).move(940, 240); //.animate().rotate(360, 256 + 940, 256 + 240).loop();
let fan2 = pumpGroup.image("https://cdn0.iconfinder.com/data/icons/screw/512/fan-ventilator-propeller-rotor-motion-512.png").scale(0.05).move(140, 240);
//
// 1 = slave, 2 = master
let fant1Runner = fan1.animate(1500).ease("-").rotate(360, 256 + 940, 256 + 240).loop().timeline().pause();
let fant2Runner = fan2.animate(1500).ease("-").rotate(360, 256 + 140, 256 + 240).loop().timeline().pause();
setInterval(() => {
fant2Runner.play();
fant1Runner.play();
setTimeout(() => {
fant2Runner.pause();
fant1Runner.pause();
}, 2500)
}, 5000);
draw.use(pumpGroup).move(10, 10).click(() => {
alert("Clicked on pump!");
});
});
</script>
</head>
<body>
<div id="app">
<div id="schematic"></div>
</div>
</body>
</html>
For demsonstation i create a minimal snippet.
The fans start spinning after 5sec, run then for 2.5 sec and stops.
As written above, when the fans spin, no click fires.
Thanks for any advise.

Controls an animation using a progress bar

I am using these libraries
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>
I am making an animation that I try to synchronize with a progress bar. My animation would last a total of 15 seconds, and would have more elements.
For this example, I am making an animation where I have a rectangle and a circle.
var rectangle = vis.append("rect")
var circle=vis.append("circle");
For each element I define its animation properties in a json specifying a starting point (.begin) and an end point (.destiny), also a delay (.delay) then the transition is generated with the definition of those properties for each of them. an example of this json is this:
circleValues={
"delay":4000,
"duration":3000,
"begin": {
"cx": 250,
"r":20,
"fill": "blue"
},
"destiny": {
"cx": 0,
"fill": "orane"
}
}
So for the case of the circle, I will generate an animation that will start with these properties:
//circleValues.begin
"begin": {
"cx": 250,
"r":20,
"fill": "blue"
}
and a transition will be generated where in the end it will end with these properties:
//circleValues.destiny
"destiny": {
"cx": 0,
"fill": "orange"
}
The total animation will be in the course of 15 seconds, in the case of the circle and according to the properties that I defined, it means that it trasntion will start in 4 seconds, for that it will have a delay of 4000 (circleValues.delay) and will last (circleValues.duration) seconds.
Then I have a function called initAnimation() where I execute this transition for each element in the style:
function initAnimation(percentage){
//circle properties
circle.styles(circleValues.begin)
.transition()
.delay(circleValues.delay)
.duration(circleValues.duration)
.ease(d3.easeLinear())
.styles(circleValues.destiny)
}
I want to control the animation using a progress bar in this case a progressbar that I define using:
//15 seconds, min value is 0 seconds, max value is 15 seconds, step=1
<input id="progressbar" type="range" min="0" max="15" step="1" value="0" oninput="setPercentValue(+event.target.value)">
and I make the progress bar automatically move every second with the following code, I also have 2 buttons to stop animation and resume animation.
var duration=15000; //15 seconds duration animation
var second=0; // current value of progress var
var interval; // instance of setInterval
function pause(){
clearInterval(interval);
}
function play(){
moveProgressBar();
}
function moveProgressBar(){
interval= setInterval(()=>{
console.log(second);
second++;
document.getElementById("progressbar").value=second;
if(second==15){
clearInterval(interval);
}
},1000)
}
and I execute all my code with the invocation of these 2 lines.
initAnimation(0);
moveProgressBar();
The problem is that I don't know how to make it so that according to the selected time of my progress bar, it corresponds to the current moment in which each element of my animation should be. I would like that depending on the value you select when moving the progress bar, it corresponds to the behaviors that I defined for each of my elements, even if I pause or resume the animation. This is beyond my knowledge at d3.js.
How can I do it?
d3.select("#visualization").append('svg');
var vis = d3.select("svg").attr("width", 800).attr("height", 150).style("border", "1px solid red");
var duration=15000; //15 seconds duration animation
var second=0; // current value of progress var
var interval; // instance of setInterval
function pause(){
clearInterval(interval);
}
function play(){
moveProgressBar();
}
function moveProgressBar(){
interval= setInterval(()=>{
second++;
document.getElementById("progressbar").value=second;
if(second==15){
clearInterval(interval);
}
},1000)
}
function setPercentValue(percentage) {
second=percentage;
rectangle.interrupt();
circle.interrupt();
initAnimation(percentage);
}
var rectValues={
"delay":2000,
"duration":5000,
"begin": {
"x": 0,
"y": 0,
"height": 70,
"width": 100,
"opacity": 1,
"fill": "red"
},
"destiny": {
"x": 250,
"y": 1,
"height": 100,
"width": 120,
"opacity": 0.8,
"fill": "green"
}
}
var rectangle = vis.append("rect")
var circle=vis.append("circle");
var circleValues={
"delay":4000,
"duration":3000,
"begin": {
"cx": 250,
"r":20,
"fill": "blue"
},
"destiny": {
"cx": 0,
"fill": "orange"
}
}
function initAnimation(percentage){
//rectangle properties
rectangle.styles(rectValues.begin)
.transition()
.delay(rectValues.delay)
.duration(rectValues.duration)
.ease(t => d3.easeLinear(percentage + t * (1 - percentage)))
.styles(rectValues.destiny)
//circle properties
circle.styles(circleValues.begin)
.transition()
.delay(circleValues.delay)
.duration(circleValues.duration)
.ease(t => d3.easeLinear(percentage + t * (1 - percentage)))
.styles(circleValues.destiny)
}
initAnimation(0);
moveProgressBar();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>
<div id="visualization"></div>
<!--<input type="range" min="0" max="1" step="0.1" value="0" oninput="setPercentValue(+event.target.value)">-->
<input id="progressbar" type="range" min="0" max="15" step="1" value="0" oninput="setPercentValue(+event.target.value)">
<button onclick="pause()">
pause
</button>
<button onclick="play()">
play
</button>
As you've specified in your post, your animations are composed of a delay followed by a transition.
If you want to display the result of such animation at some fixed time T (as determined by the progressbar value), you need to evaluate the following conditions:
if T is less than the delay, then you are still waiting for the transition to start. The shape that you're animating still has all attributes set to the begin values and you only need to make the delay shorter
if T is more than the delay, but less then delay+transition duration, then the shape is already in the transition phase. In this case, you need to determine how far in translation you are at time T and interpolate the attribute values (so the values will be something between begin and destiny), also the transition duration needs to be shortened
if T is more than the delay, then the transition is already completed. In this case, the attribute values will be set to destiny values
This logic is encapsulated in the function interpolateValuesAtTime below. This function, in addition to calculating new delay,duration,begin and destiny values, it also creates a new easing function, which calculates how the easing should continue from time T onward (details about how the modified easing functions works are described in detail on this page)
You can test it in the snippet below.
Here are few notes regarding its functionality:
after clicking on the progressbar, the animation will be shown at given time (the animation will be paused, press play to start it)
also, after pressing play, the animation starts from the time corresponding to the progress bar (and since the progress bar counts only whole seconds, that implies that if you pause & play the animation, it may seem to jump back a little)
const progressbar = document.getElementById('progressbar');
d3.select("#visualization").append('svg');
const vis = d3.select("svg").attr("width", 800).attr("height", 150).style("border", "1px solid red");
const rectangle = vis.append("rect")
const circle = vis.append("circle");
let second = 0;
let interval;
const rectValues = {
"delay": 2000,
"duration": 5000,
"begin": {
"x": 0,
"y": 0,
"height": 70,
"width": 100,
"opacity": 1,
"fill": "red"
},
"destiny": {
"x": 250,
"y": 1,
"height": 100,
"width": 120,
"opacity": 0.8,
"fill": "green"
}
};
const circleValues = {
"delay": 4000,
"duration": 3000,
"begin": {
"cx": 250,
"r": 20,
"fill": "blue"
},
"destiny": {
"cx": 0,
"fill": "orange"
}
}
function resumedEasingFunction(easingFunction, progress) {
return function (xAfterResume) {
const xOriginal = d3.scaleLinear()
.domain([0, 1])
.range([progress, 1])
(xAfterResume);
return d3.scaleLinear()
.domain([easingFunction(progress), 1])
.range([0, 1])
(easingFunction(xOriginal));
};
}
function interpolateValuesAtTime(values, easingFunction, time) {
const interpolatedValues = JSON.parse(JSON.stringify(values));;
let progress;
if (values.delay >= time) {
//the initial delay has not yet elapsed
interpolatedValues.delay = values.delay - time;
progress = 0;
} else if (values.delay + values.duration >= time) {
//the animation is running
interpolatedValues.delay = 0;
interpolatedValues.duration = values.delay + values.duration - time;
progress = (values.duration - interpolatedValues.duration) / values.duration;
for (let key in values.begin) {
const startValue = values.begin[key];
if (key in values.destiny) {
const endValue = values.destiny[key];
const interpolator = d3.interpolate(startValue, endValue);
interpolatedValues.begin[key] = interpolator(progress);
}
}
} else {
//the animation has already ended
interpolatedValues.delay = 0;
interpolatedValues.duration = 0;
progress = 1;
for (let key in values.destiny) {
interpolatedValues.begin[key] = values.destiny[key];
}
}
interpolatedValues.easingFunction = resumedEasingFunction(easingFunction, progress);
return interpolatedValues;
}
function play() {
startAnimation(progressbar.value * 1000);
startMovingProgressbar();
}
function pause() {
stopMovingProgressBar();
pauseAnimation();
}
function updateTimeBasedOnProgressbar() {
stopMovingProgressBar();
second = progressbar.value;
startAnimation(1000 * second);
pause();
}
function stopMovingProgressBar(){
clearInterval(interval);
}
function startMovingProgressbar() {
clearInterval(interval);
interval = setInterval(() => {
second++;
progressbar.value = second;
if (second == 15) {
clearInterval(interval);
}
}, 1000)
}
function pauseAnimation() {
pauseShape(rectangle);
pauseShape(circle);
}
function pauseShape(shape) {
shape.transition()
.duration(0)
}
function startAnimation(miliseconds) {
//rectangle properties
startShapeAnimation(rectangle, rectValues, miliseconds);
//circle properties
startShapeAnimation(circle, circleValues, miliseconds);
}
function startShapeAnimation(shape, shapeValues, miliseconds) {
const valuesAtTime = interpolateValuesAtTime(shapeValues, d3.easeLinear, miliseconds);
shape.attrs(valuesAtTime.begin)
.transition()
.duration(0)
.attrs(valuesAtTime.begin)
.transition()
.delay(valuesAtTime.delay)
.duration(valuesAtTime.duration)
.ease(valuesAtTime.easingFunction)
.attrs(valuesAtTime.destiny);
}
updateTimeBasedOnProgressbar();
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>
</head>
<body>
<div id="visualization"></div>
<input id="progressbar" type="range" min="0" max="15" step="1" value="0" oninput="updateTimeBasedOnProgressbar()">
<button onclick="pause()">pause</button>
<button onclick="play()">play</button>
</script>
</body>
</html>

dc.js average value over the summed field

I have the following data array:
ID Name Number
---- ------ --------
1 G 1
1 G 2
1 F 3
I want to do the following conversion to calculate the average, but I don’t know how to do it.
ID Name Number_sum
---- ------ ------------
1 G 3
1 F 3
after summing calculate the average
ID Number_avg
---- ------------
1 3
If you do not pre-sum, then the average value is calculated incorrectly:
ID Number_avg
---- ------------
1 2
I want to calculate the average value for each ID, but with an even field "Name".
Next, I plan to build a graph for each ID. I have a road identifier - 1. This road consists of 2 sections: G and F. Moreover, section G is divided into 2 more small sections, 1 and 2 km each.
If we consider the usual average value, then we get the average value over the maximum section of the value - a sub-section of the road. But I want to make a calculation based on the average value of the road sections.
<!DOCTYPE html>
<html lang="en">
<head>
<title>dc.js</title>
<meta charset="UTF-8">
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/d3.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/crossfilter.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/dc.js"></script>
</head>
<body>
<div id ="test"></div>
<script type="text/javascript">
//dc.js
var inlineND = new dc.NumberDisplay("#test");
//data
var array1 = [
{"ID": 1, "Name": "G", "Number": 1},
{"ID": 1, "Name": "G", "Number": 2},
{"ID": 1, "Name": "F", "Number": 3}
];
var make_calc = function() {
var ndx = crossfilter(array1), //
Dimension = ndx.dimension(function(d) {return d.ID;}),
DimensionGroup = Dimension.group().reduce(reduceAdd, reduceRemove, reduceInitial);
function reduceAdd(p, v) {
++p.count;
p.total += v.Number;
return p;
}
function reduceRemove(p, v) {
--p.count;
p.total -= v.Number;
return p;
}
function reduceInitial() {
return {count: 0, total: 0};
}
inlineND
.group(DimensionGroup)
.valueAccessor(function(p) { return p.value.count > 0 ? p.value.total / p.value.count : 0; });
dc.renderAll();
//console.log(DimensionGroup);
};
make_calc();
</script>
</body>
</html>
In order to calculate the average, taking into account the "Name" field, it is necessary to consider the unique occurrence of this field in the reduce function. As a result, when calculating the average value, divide the sum of values ​​by the number of unique values ​​in the "Name" field
<!DOCTYPE html>
<html lang="en">
<head>
<title>dc.js</title>
<meta charset="UTF-8">
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/d3.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/crossfilter.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/dc.js"></script>
</head>
<body>
<div id ="test"></div>
<script type="text/javascript">
//dc.js
var inlineND = new dc.NumberDisplay("#test");
//data
var array1 = [
{"ID": 1, "Name": "G", "Number": 1},
{"ID": 1, "Name": "G", "Number": 2},
{"ID": 1, "Name": "F", "Number": 3}
];
var make_calc = function() {
var ndx = crossfilter(array1), //
Dimension = ndx.dimension(function(d) {return d.ID;}),
DimensionGroup = Dimension.group().reduce(reduceAdd, reduceRemove, reduceInitial);
function reduceAdd(p, v) {
++p.count;
p.total += v.Number;
if(v.Name in p.Names){
p.Names[v.Name] += 1
}
else{
p.Names[v.Name] = 1;
p.Name_count++;
};
return p;
}
function reduceRemove(p, v) {
--p.count;
p.total -= v.Number;
p.Names[v.Name]--;
if(p.Names[v.Name] === 0){
delete p.Names[v.Name];
p.Name_count--;
};
return p;
}
function reduceInitial() {
return {count: 0, total: 0, Name_count: 0, Names: {}};
}
inlineND
.group(DimensionGroup)
.valueAccessor(function(p) { return p.value.Name_count > 0 ? p.value.total / p.value.Name_count : 0; });
dc.renderAll();
//console.log(DimensionGroup);
};
make_calc();
</script>
</body>
</html>
I'm not sure, but are you looking for something like this?
const arr = [
{id: 1, name: 'G', number: 1},
{id: 2, name: 'G', number: 2},
{id: 3, name: 'F', number: 3}
]
const res = arr.reduce((acc, e) => {
const idx = acc.findIndex(x => x.name === e.name)
if (idx !== -1) {
acc[idx].number += e.number
} else {
acc.push(e)
}
return acc
}, [])
console.log(res)
I'm not sure if I completely understand, but if you want to group by Name, sum, and and then take the average of all groups, you could put your dimension on Name and use regular reduceSum:
var ndx = crossfilter(array1), //
Dimension = ndx.dimension(function(d) {return d.Name;}),
DimensionGroup = Dimension.group().reduceSum(d => d.Number);
Then pass a "fake groupAll" which returns all the rows from the group to the number display, and calculate the average in the value accessor:
.group({value: () => DimensionGroup.all()})
.valueAccessor(a => a.length === 0 ? 0 : d3.sum(a, ({value}) => value) / a.length);
<!DOCTYPE html>
<html lang="en">
<head>
<title>dc.js</title>
<meta charset="UTF-8">
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/d3.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/crossfilter.js"></script>
<script type="text/javascript" src="https://dc-js.github.io/dc.js/js/dc.js"></script>
</head>
<body>
<div id ="test"></div>
<script type="text/javascript">
//dc.js
var inlineND = new dc.NumberDisplay("#test");
//data
var array1 = [
{"ID": 1, "Name": "G", "Number": 1},
{"ID": 1, "Name": "G", "Number": 2},
{"ID": 1, "Name": "F", "Number": 3}
];
var make_calc = function() {
var ndx = crossfilter(array1), //
Dimension = ndx.dimension(function(d) {return d.Name;}),
DimensionGroup = Dimension.group().reduceSum(d => d.Number);
inlineND
.group({value: () => DimensionGroup.all()})
.valueAccessor(a => a.length === 0 ? 0 : d3.sum(a, ({value}) => value) / a.length);
dc.renderAll();
//console.log(DimensionGroup);
};
make_calc();
</script>
</body>
</html>

Want to highlight/change color of certain words in Ace Editor?

I am setting text using the following:
var someString = 'Mark has solved the problem';
editor.getSession().setValue(someString);
In above case, Need only 'Mark' to appear in blue color.
Is there a way to load html tags in ace editor or manipulate styling of certain words?
You can create custom highlight rules to do this
<!DOCTYPE html>
<html lang="en">
<head>
<title>ACE in Action</title>
<meta charset="utf-8">
<style type="text/css" media="screen">
#editor {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.ace_customTokenName {color: darkred}
</style>
</head>
<body>
<div id="editor" style="height: 500px; width: 800px"></div>
<script src="https://ajaxorg.github.io/ace-builds/src/ace.js"></script>
<script>
define('ace/mode/custom', [], function(require, exports, module) {
var oop = require("ace/lib/oop");
var TextMode = require("ace/mode/text").Mode;
var Tokenizer = require("ace/tokenizer").Tokenizer;
var CustomHighlightRules = require("ace/mode/custom_highlight_rules").CustomHighlightRules;
var Mode = function() {
this.HighlightRules = CustomHighlightRules;
};
oop.inherits(Mode, TextMode);
(function() {
}).call(Mode.prototype);
exports.Mode = Mode;
});
define('ace/mode/custom_highlight_rules', [], function(require, exports, module) {
var oop = require("ace/lib/oop");
var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules;
var CustomHighlightRules = function() {
var keywordMapper = this.createKeywordMapper({
"variable.language": "this",
"keyword": "Mark|Ben|Bill",
"constant.language": "true|false|null",
// it is also possible to use css, but that may conflict with themes
"customTokenName": "problem"
}, "text", true);
this.$rules = {
"start": [
{
regex: "\\w+\\b",
token: keywordMapper
},
]
};
this.normalizeRules()
};
oop.inherits(CustomHighlightRules, TextHighlightRules);
exports.CustomHighlightRules = CustomHighlightRules;
});
var editor = ace.edit("editor");
editor.session.setMode("ace/mode/custom");
var someString = 'Mark has solved the problem';
editor.session.setValue(someString);
</script>
</body>
</html>

javascript canvasjs Get data points dynamically from file.txt

Objective:
I want to get data from file.txt thats is saved local in /var/www/html/file.txt and use it for the doughnut chart on my webpage dynamically on a interval of 2 seconds
file.txt only has one entry and looks like:
34
the javascript i have tried:
$.get("file.txt", function(data) {
var x = 0;
var allLines = data.split('\n');
if(allLines.length > 0) {
for(var i=0; i< allLines.length; i++) {
dataPoints.push({y: parseInt(allLines[i])});
x += .25;
}
}
var chart = new CanvasJS.Chart("chartContainer",{
title :{
text: "Chart using Text File Data"
},
data: [{
type: "doughnut",
dataPoints : dataPoints,
}]
});
chart.render();
});
}
Entire html looks like
<!DOCTYPE html>
<html>
<head>
<title>Chart using txtfile Data</title>
<script type="text/javascript" src="http://canvasjs.com/assets/script /jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="http://canvasjs.com/assets/script/canvasjs.min.js"></script>
</head>
<body>
<script type="text/javascript">
$.get("graph.txt", function(data) {
var xVal = 0;
var allLines = data.split('\n');
var chart = new CanvasJS.Chart("chartContainer",{
title :{
text: "Chart using Text File Data"
},
data: [{
type: "line",
dataPoints : [function()
{
if(allLines.length > 0) {
for(var i=0; i< allLines.length; i++) {
xVal +=.25;
dataPoints.push({x : xVal, y: parseInt(allLines[i])});
}
}
}]
}]
});
chart.render();
},'text');
</script>
<script type="text/javascript" src="canvasjs.min.js"></script>
<div id="chartContainer" style="height: 300px; width: 100%;"></div>
</body>
</html>
I believe something like this may work for you. You may have to use Firefox as Chrome doesn't like cross origin requests
First, dataPoints was undefined, I moved the code into a function inside dataPoints. I changed your variable name from x to xVal. Added the 'text' word so the $get knows what format it's reading and also there was an extra bracket it seemed. Give this a try.
$.get("graph.txt", function(data) {
var xVal = 0;
var allLines = data.split('\n');
var dps = [];
for(var i=0; i< allLines.length; i++) {
xVal +=.25;
dps.push({x : xVal, y: Number(allLines[i])});
}
var chart = new CanvasJS.Chart("chartContainer",{
title :{
text: "Chart using Text File Data"
},
data: [{
type: "line",
dataPoints : dps
}]
});
chart.render();
},'text');

Resources