there. I use ChartJS and customise tooltip, but have issue with position first and last tooltip's.
Look:
I suppose that in order to fix the problem, I need to use the https://www.chartjs.org/docs/latest/configuration/tooltip.html#position-modes
but, I cannot understand what the formula should be.
CodePen example - https://codepen.io/anon/pen/JzRooy
<html>
<head>
<title>Line Chart with Custom Tooltips</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.bundle.js"></script>
<script>
window.chartColors = {
red: 'rgb(255, 99, 132)',
orange: 'rgb(255, 159, 64)',
yellow: 'rgb(255, 205, 86)',
green: 'rgb(75, 192, 192)',
blue: 'rgb(54, 162, 235)',
purple: 'rgb(153, 102, 255)',
grey: 'rgb(231,233,237)'
};
window.randomScalingFactor = function() {
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
}
</script>
<style>
canvas{
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
#chartjs-tooltip {
opacity: 1;
position: absolute;
background: rgba(0, 0, 0, .7);
color: white;
border-radius: 3px;
-webkit-transition: all .1s ease;
transition: all .1s ease;
pointer-events: none;
-webkit-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
.chartjs-tooltip-key {
display: inline-block;
width: 10px;
height: 10px;
margin-right: 10px;
}
</style>
</head>
<body>
<canvas id="chart"/>
<script>
Chart.defaults.global.pointHitDetectionRadius = 1;
var customTooltips = function(tooltip) {
var tooltipEl = document.getElementById('chartjs-tooltip');
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'chartjs-tooltip';
tooltipEl.innerHTML = "<div class='wrapper'></div>"
document.body.appendChild(tooltipEl);
}
// Hide if no tooltip
if (tooltip.opacity === 0) {
tooltipEl.style.opacity = 0;
return;
}
// Set caret Position
tooltipEl.classList.remove('above', 'below', 'no-transform');
if (tooltip.yAlign) {
tooltipEl.classList.add(tooltip.yAlign);
} else {
tooltipEl.classList.add('no-transform');
}
function getBody(bodyItem) {
return bodyItem.lines;
}
// Set Text
if (tooltip.body) {
var titleLines = tooltip.title || [];
var bodyLines = tooltip.body.map(getBody);
var innerHtml = '';
titleLines.forEach(function(title) {
innerHtml += '<span style="margin-bottom: 10px;display: inline-block;">' + title + '</span>';
});
innerHtml += '<div style="display: flex;flex-direction: row;">';
bodyLines.forEach(function(body, i) {
var parts = body[0].split(':');
innerHtml += '<div style="display: flex;flex-direction: column;margin-right: 10px;font-size: 12px;">';
innerHtml += '<span>' + parts[0].trim() + '</span>';
innerHtml += '<b>' + parts[1].trim() + '</b>';
innerHtml += '</div>';
});
innerHtml += '</div>';
var root = tooltipEl.querySelector('.wrapper');
root.innerHTML = innerHtml;
}
var canvas = this._chart.canvas;
tooltipEl.style.opacity = 1;
tooltipEl.style.left = canvas.offsetLeft + tooltip.caretX + 'px';
tooltipEl.style.top = canvas.offsetTop + tooltip.caretY + 'px';
tooltipEl.style.fontFamily = tooltip._fontFamily;
tooltipEl.style.fontSize = tooltip.fontSize;
tooltipEl.style.fontStyle = tooltip._fontStyle;
tooltipEl.style.padding = "10px";
tooltipEl.style.border = "1px solid #B4B6C1";
tooltipEl.style.backgroundColor = "#FFFFFF";
tooltipEl.style.color = "#4C4F59";
tooltipEl.style.fontFamily = '"open sans", "helvetica neue", "arial", "sans-serif"';
};
var lineChartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
borderColor: window.chartColors.red,
pointBackgroundColor: window.chartColors.red,
fill: false,
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor()
]
}, {
label: "My Second dataset",
borderColor: window.chartColors.blue,
pointBackgroundColor: window.chartColors.blue,
fill: false,
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor()
]
}]
};
window.onload = function() {
var chartEl = document.getElementById("chart");
window.myLine = new Chart(chartEl, {
type: 'line',
data: lineChartData,
options: {
title:{
display:true,
text:'Chart.js Line Chart - Custom Tooltips'
},
tooltips: {
enabled: false,
mode: 'nearest',
position: 'average',
intersect: false,
custom: customTooltips
}
}
});
};
</script>
</body>
</html>
New modes can be defined by adding functions to the Chart.Tooltip.positioners map (DOC). This function returns the x and y position for the tooltip.
You can add a custom one to adjust the x at an offset.
One way to do this would be to be:
//register custome positioner
Chart.Tooltip.positioners.custom = function(elements, position) {
if (!elements.length) {
return false;
}
var offset = 0;
//adjust the offset left or right depending on the event position
if (elements[0]._chart.width / 2 > position.x) {
offset = 20;
} else {
offset = -20;
}
return {
x: position.x + offset,
y: position.y
}
}
Fiddle example that I created
I hope it helps.
I had the same issue and I didn't find a good solution, so I had to dot it myself.
Actually, it's simple than I thought, wish it helps someone.
Demo: https://codepen.io/themustafaomar/pen/wvWZrod
const labels = ["1 April","2 April","3 April","4 April","5 April","6 April","7 April","8 April","9 April","10 April","11 April","12 April","13 April","14 April","15 April","16 April","17 April","18 April","19 April","20 April","21 April","22 April","23 April","24 April","25 April","26 April","27 April","28 April","29 April","30 April","31 April"]
const data = [ 95, 57, 72, 54, 73, 53, 98, 75, 52, 93, 50, 65, 99, 67, 77, 61, 74, 65, 86, 92, 64, 89, 82, 62, 64, 89, 59, 75, 56, 63 ];
function customTooltips(tooltipModel) {
// Tooltip Element
var tooltipEl = document.getElementById("chartjs-tooltip");
const yAlign = tooltipModel.yAlign;
const xAlign = tooltipModel.xAlign;
// Create element on first render
if (!tooltipEl) {
tooltipEl = document.createElement("div");
tooltipEl.id = "chartjs-tooltip";
tooltipEl.innerHTML = "<table></table>";
document.body.appendChild(tooltipEl);
}
// Hide if no tooltip
if (tooltipModel.opacity === 0) {
tooltipEl.style.opacity = 0;
return;
}
// Set caret Position
tooltipEl.classList.remove("top", "bottom", "center", "left", "right");
if (tooltipModel.yAlign || tooltipModel.xAlign) {
tooltipEl.classList.add(tooltipModel.yAlign);
tooltipEl.classList.add(tooltipModel.xAlign);
}
// Set Text
if (tooltipModel.body) {
var titleLines = tooltipModel.title || [];
var bodyLines = tooltipModel.body.map((bodyItem) => {
return bodyItem.lines;
});
var innerHtml = "<thead>";
titleLines.forEach(function (title) {
innerHtml += '<tr><th><div class="mb-1">' + title + "</div></th></tr>";
});
innerHtml += "</thead><tbody>";
bodyLines.forEach((body, i) => {
var colors = tooltipModel.labelColors[i];
// var style = 'background-color:' + colors.borderColor
var style =
"background-color:" + this._chart.data.datasets[i].borderColor;
var value = tooltipModel.dataPoints[i].value;
var label = this._chart.data.datasets[i].label;
style += "; border-color:" + colors.borderColor;
style += "; border-color:" + this._chart.data.datasets[i].borderColor;
style += "; border-width: 2px";
var span =
'<span class="chartjs-tooltip-key" style="' + style + '"></span>';
innerHtml += `<tr><td> ${span} $${value}K </td></tr>`;
});
innerHtml += "</tbody>";
var tableRoot = tooltipEl.querySelector("table");
tableRoot.innerHTML = innerHtml;
}
// Tooltip height and width
const { height, width } = tooltipEl.getBoundingClientRect();
// Chart canvas positions
const positionY = this._chart.canvas.offsetTop;
const positionX = this._chart.canvas.offsetLeft;
// Carets
const caretY = tooltipModel.caretY;
const caretX = tooltipModel.caretX;
// Final coordinates
let top = positionY + caretY - height;
let left = positionX + caretX - width / 2;
let space = 8; // This for making space between the caret and the element.
// yAlign could be: `top`, `bottom`, `center`
if (yAlign === "top") {
top += height + space;
} else if (yAlign === "center") {
top += height / 2;
} else if (yAlign === "bottom") {
top -= space;
}
// xAlign could be: `left`, `center`, `right`
if (xAlign === "left") {
left = left + width / 2 - tooltipModel.xPadding - space / 2;
if (yAlign === "center") {
left = left + space * 2;
}
} else if (xAlign === "right") {
left -= width / 2;
if (yAlign === "center") {
left = left - space;
} else {
left += space;
}
}
// Display, position, and set styles for font
tooltipEl.style.opacity = 1;
// Left and right
tooltipEl.style.top = `${top}px`;
tooltipEl.style.left = `${left}px`;
// Font
tooltipEl.style.fontFamily = tooltipModel._bodyFontFamily;
tooltipEl.style.fontSize = tooltipModel.bodyFontSize + "px";
tooltipEl.style.fontStyle = tooltipModel._bodyFontStyle;
// Paddings
tooltipEl.style.padding =
tooltipModel.yPadding + "px " + tooltipModel.xPadding + "px";
}
// Chart
new Chart("chart", {
type: "line",
data: {
labels,
datasets: [
{
label: "Custom tooltip demo",
borderColor: "#f66",
backgroundColor: "transparent",
lineTension: 0,
borderWidth: 1.5,
pointRadius: 2,
data
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
legend: { display: false },
scales: {
// YAxes
yAxes: [{ display: false }],
// XAxes
xAxes: [
{
display: false,
gridLines: { display: false },
ticks: {
padding: 20,
autoSkipPadding: 30,
maxRotation: 0
}
}
]
},
tooltips: {
enabled: false,
intersect: false,
mode: "index",
position: "average",
custom: customTooltips
}
}
});
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.chartjs-wrapper {
height: 90px;
width: 300px;
margin: 25px auto 0;
border: 1px solid #e6e6e6;
}
#chartjs-tooltip {
opacity: 1;
position: absolute;
color: #fff;
background-color: #000;
border-radius: 6px;
transition: all 0.25s ease-in-out;
pointer-events: none;
}
#chartjs-tooltip:after {
content: "";
display: block;
position: absolute;
margin: auto;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
}
/* Top center */
#chartjs-tooltip.top.center:after {
border-bottom-color: #000;
top: -11px;
left: 0;
right: 0;
}
/* Top left */
#chartjs-tooltip.top.left:after {
border-bottom-color: #000;
left: 5px;
top: -11px;
}
/* Top right */
#chartjs-tooltip.top.right:after {
border-bottom-color: #000;
right: 5px;
top: -11px;
}
/* Bottom center */
#chartjs-tooltip.bottom.center:after {
border-top-color: #000;
bottom: -11px;
right: 0;
left: 0;
}
/* Bottom left */
#chartjs-tooltip.bottom.left:after {
border-top-color: #000;
bottom: -11px;
}
/* Bottom right */
#chartjs-tooltip.bottom.right:after {
border-top-color: #000;
bottom: -11px;
right: 5px;
}
/* Center left */
#chartjs-tooltip.center.left:after {
border-right-color: #000;
margin: auto;
left: -11px;
bottom: 0;
top: 0;
}
/* Center right */
#chartjs-tooltip.center.right:after {
border-left-color: #000;
margin: auto;
right: -11px;
bottom: 0;
top: 0;
}
.chartjs-tooltip-key {
display: inline-block;
border-radius: 50%;
width: 10px;
height: 10px;
margin-right: 7px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<div class="chartjs-wrapper">
<canvas id="chart"></canvas>
</div>
I did this:
Subtract the pixels by way of centering it or putting it in the position.
tooltipEl.style.left = canvas.offsetLeft + tooltip.caretX - 55 + 'px';
U use this in addition to externalTooltipHandler from chartJs
This will modify position only for latest tooltip
var currentTooltip = tooltip.dataPoints[0].dataIndex;
var keys = Object.keys(tooltip.dataPoints[0].dataset.data);
var latestTooltip = keys[keys.length - 1];
if (currentTooltip == latestTooltip) {
tooltipEl.style.left = chart.canvas.offsetLeft + tooltip.caretX - 70 + "px";
} else {
tooltipEl.style.left = positionX + tooltip.caretX + "px";
}
Related
I'm trying to realise an infinite up and down move of the platform . How can I modify the code to get this thing ? I have only managed to have just one un-down movement . I know that I could do this with CSS animations but I would like to modify my code .
var n = 0;
var grid = document.querySelector('.grid');
function move() {
const pixels = [200, 196, 192, 188, 184, 180, 176, 172, 168, 164, 160, 164, 168, 172, 176, 180];
const style = grid.style.bottom
const computedStyle = window.getComputedStyle(grid)
console.log('bottom from computed style', computedStyle.bottom)
grid.style.bottom = pixels[n] + 'px';
n++;
}
move();
setInterval(move, 90);
.grid {
background-color: blue;
height: 20px;
width: 100px;
left: 100px;
bottom: 200px;
position: absolute;
}
<div class="grid"></div>
The function is not looping because you are not resetting n - after the first up down movement, n goes out of bounds, grid.style.bottom = pixels[n] + 'px'; tries to set the stile to undefined +'px' and fails, and the bar stays where it is.
I added n = n % pixels.length; to reset n once it goes out of bounds.
var n = 0;
var grid = document.querySelector('.grid');
function move() {
const pixels = [200, 196, 192, 188, 184, 180, 176, 172, 168, 164, 160, 164, 168, 172, 176, 180];
const style = grid.style.bottom
const computedStyle = window.getComputedStyle(grid)
console.log('bottom from computed style', computedStyle.bottom)
n = n % pixels.length;
grid.style.bottom = pixels[n] + 'px';
n++;
}
move();
setInterval(move, 90);
.grid {
background-color: blue;
height: 20px;
width: 100px;
left: 100px;
bottom: 200px;
position: absolute;
}
<div class="grid"></div>
You can have a Boolean checking once you get at the end of your array and once you do you start to decrement your n variable. This way it will go from 200px -> 180px -> 200px
let grid = document.querySelector(".grid");
let n = 0;
let bool = true
function move() {
const pixels = [200, 196, 192, 188, 184, 180, 176, 172, 168, 164, 160, 164, 168, 172, 176, 180]
const style = grid.style.bottom;
const computedStyle = window.getComputedStyle(grid);
console.log("bottom from computed style", computedStyle.bottom);
grid.style.bottom = pixels[n] + "px";
if(n === pixels.length - 1 ){
bool = false
} else if(n === 0){
bool = true
}
bool ? n++ : n--
}
move();
setInterval(move, 90);
.grid {
background-color: blue;
height: 20px;
width: 100px;
left: 100px;
bottom: 200px;
position: absolute;
}
<div class="grid"></div>
Instead of incrementing in an interval, better compute the position based on the time passed since start.
According to your pixels array and your interval, you move 40px up/down every 900ms. (10 steps of 4px / 90ms)
const start = Date.now()
function move(){
const time = Date.now() - start;
let t = time / 900; // the time passed in terms of up/down strokes
// t = t % 1; // turns this into a sawtooth pattern, just up-strokes
// not what we want, we want every other stroke to be a down-stroke.
t = t&1 ? // every other stroke
(1 - t%1) : // move down
(t%1); // otherwise mode up
// now we have out position as a percentage value 0..1;
// let's compute the pixels.
const pos = 200 - 40*t; // start at 200px and travel a fraction of 40px down.
grid.style.bottom = pos + "px";
// rAF is way smoother than your 90ms interval.
requestAnimationFrame(move);
}
move();
const DURATION = 900;
const grid = document.querySelector('.grid');
const start = 0;
function move() {
let t = (Date.now() - start) / 900;
t = t&1 ? 1-t%1 : t%1; // zig-zag
//t = t%1; // sawtooth; try this instead of the zig-zag and see/understand the difference.
//add some easing; try it.
//t = t*t*(3-2*t);
grid.style.bottom = 200 - 40*t + "px";
requestAnimationFrame(move);
}
move();
.grid {
background-color: blue;
height: 20px;
width: 100px;
left: 100px;
bottom: 200px;
position: absolute;
}
<div class="grid"></div>
I am trying to make a box with a nice looking animated gradient.
I can not use the animate function to change the gradient so I try to do it with JavaScript.
I would use the jQuery.css() function, but I can not address the :before and :after selectors with jQuery.
So what I am trying to do is to change the CSS code in #js-style every 100ms in an infinite loop.
But what happens is that it does update the CSS code, but it only applies it when the loop breaks.
Is there a way to tell the browser to check and apply the code every time it changes during the JavaScript loop?
$(document).ready(function() {
// Settings
var steps_per_color = 100;
var timeout = 100;
var colors = [
[255, 0, 158],
[0, 205, 255],
[42, 0, 255],
[168, 0, 253]
];
var cur_base_colors = [
0,
1,
2,
3
];
var cur_goal_colors = [
1,
2,
3,
0
];
var cur_colors = [
colors[0],
colors[1],
colors[2],
colors[3]
];
var def_colors = [
[255, 0, 158],
[0, 205, 255],
[42, 0, 255],
[168, 0, 253]
];
var step = 0;
var step_size, goal, base, text, cur_color;
while (true) {
cur_base_colors.unshift(cur_base_colors[3]);
cur_base_colors.pop();
cur_goal_colors.unshift(cur_goal_colors[3]);
cur_goal_colors.pop();
cur_colors.forEach(function(color, color_index) {
cur_colors[color_index] = colors[cur_base_colors[color_index]];
});
while (step < steps_per_color) {
cur_colors.forEach(function(color, color_index) {
color.forEach(function(item, index) {
base = colors[cur_base_colors[color_index]][index];
goal = colors[cur_goal_colors[color_index]][index];
step_size = (goal - base) / steps_per_color;
cur_color = item + step_size
cur_colors[color_index][index] = cur_color;
def_colors[color_index][index] = Math.round(cur_color);
});
});
text = ".shadow:before, .shadow:after{background: linear-gradient(135deg";
def_colors.forEach(function(item, index) {
text += ", rgb(" + item[0] + ", " + item[1] + ", " + item[2] + ")";
});
text += ");}";
console.log(text);
$('#js-style').html(text);
sleep(timeout);
step += 1;
}
break;
step = 0;
}
});
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
* {
padding: 0;
box-sizing: border-box;
}
body {
background: #555;
margin: 0;
height: 100vh;
width: 100vw;
padding: 20px;
}
.block {
position: relative;
width: 100%;
height: 100%;
color: #f2f2f2;
display: flex;
flex-direction: column;
justify-content: center;
border-radius: 5px;
}
.shadow:before,
.shadow:after {
content: '';
position: absolute;
top: -2px;
left: -2px;
width: calc(100% + 4px);
height: calc(100% + 4px);
background-size: 100%;
z-index: -1;
border-radius: 5px;
}
.shadow:after {
top: -4px;
left: -4px;
width: calc(100% + 10px);
height: calc(100% + 10px);
filter: blur(4px);
opacity: 1;
}
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<div class="block shadow"></div>
Please I need help me with tradingview js chart customization. I am having a serious issue with the chart.
Chart is not showing accurate date pull from the database into the chart
The picture above is the chart showing the accurate date of the data provided
Data pulled from the database gives the wrong date thereby gives a wrong chart.
How can I correct the chart date
var investment_charts = LightweightCharts.createChart(document.getElementById('investment_charts'), {
width: width,
height: height,
priceScale: {
scaleMargins: {
top: 0.2,
bottom: 0.2,
},
position: 'left',
borderVisible: true,
},
timeScale: {
borderVisible: false,
},
grid: {
horzLines: {
color: '#eee',
},
vertLines: {
color: '#ffffff',
},
},
crosshair: {
horzLine: {
visible: false,
labelVisible: false
},
vertLine: {
visible: true,
style: 0,
width: 1,
color: 'rgba(32, 38, 46, 0.1)',
labelVisible: false,
}
},
});
// chart.resize(320, 250);
var series = investment_charts.addAreaSeries({
topColor: 'rgba(0, 120, 255, 0.2)',
bottomColor: 'rgba(0, 120, 255, 0.0)',
lineColor: 'rgba(0, 120, 255, 1)',
lineWidth: 3
});
series.setData([
<?php
foreach($user->historychart as $key=>$value){
if($value->investment_amount != null){
echo '{ time:'.$value->created_at->format('Y-m-d').', value:'. $value->investment_amount.'},';
}
}
?>
]);
function businessDayToString(businessDay) {
return new Date(Date.UTC(businessDay.year, businessDay.month - 1, businessDay.day, 0, 0, 0)).toLocaleDateString();
}
var toolTipWidth = 96;
var toolTipHeight = 40;
var toolTipMargin = 15;
var priceScaleWidth = 10;
var toolTip = document.createElement('div');
toolTip.className = 'floating-tooltip-1';
document.getElementById('investment_charts').appendChild(toolTip);
// update tooltip
investment_charts.subscribeCrosshairMove(function(param) {
if (!param.time || param.point.x < 0 || param.point.x > width || param.point.y < 0 || param.point.y > height) {
toolTip.style.display = 'none';
return;
}
var dateStr = LightweightCharts.isBusinessDay(param.time)
? businessDayToString(param.time)
: new Date(param.time * 1000).toLocaleDateString();
toolTip.style.display = 'block';
var price = param.seriesPrices.get(series);
toolTip.innerHTML = '<div style="color: rgba(0, 120, 255, 0.9)">⬤ Naira</div>' +
'<div style="font-size: 24px; margin: 4px 0px; color: #20262E">' + (Math.round(price * 100) / 100).toFixed(2) + '</div>' +
'<div>' + dateStr + '</div>';
var left = param.point.x;
if (left > width - toolTipWidth - toolTipMargin) {
left = width - toolTipWidth;
} else if (left < toolTipWidth / 2) {
left = priceScaleWidth;
}
toolTip.style.left = left + 'px';
toolTip.style.top = 80 + 'px';
});
var charts = LightweightCharts.createChart(document.getElementById('charts'), {
width: width,
height: height,
priceScale: {
scaleMargins: {
top: 0.2,
bottom: 0.2,
},
position: 'left',
borderVisible: true,
},
timeScale: {
borderVisible: false,
},
grid: {
horzLines: {
color: '#eee',
},
vertLines: {
color: '#ffffff',
},
},
crosshair: {
horzLine: {
visible: false,
labelVisible: false
},
vertLine: {
visible: true,
style: 0,
width: 1,
color: 'rgba(32, 38, 46, 0.1)',
labelVisible: false,
}
},
});
// chart.resize(320, 250);
var seriesa = charts.addAreaSeries({
topColor: 'rgba(0, 120, 255, 0.2)',
bottomColor: 'rgba(0, 120, 255, 0.0)',
lineColor: 'rgba(0, 120, 255, 1)',
lineWidth: 3
});
seriesa.setData([
<?php
foreach($user->historychart as $key=>$value){
if($value->daily_profit != null){
echo '{ time:'.$value->created_at->format('Y-m-d').', value:'. $value->daily_profit.'},';
}
}
?>
]);
function businessDayToString1(businessDay) {
return new Date(Date.UTC(businessDay.year, businessDay.month - 1, businessDay.day, 0, 0, 0)).toLocaleDateString();
}
var toolTipWidth1 = 96;
var toolTipHeight1 = 40;
var toolTipMargin1 = 15;
var priceScaleWidth1 = 10;
var toolTip1 = document.createElement('div');
toolTip1.className = 'floating-tooltip-2';
document.getElementById('charts').appendChild(toolTip1);
// update tooltip
charts.subscribeCrosshairMove(function(param) {
if (!param.time || param.point.x < 0 || param.point.x > width || param.point.y < 0 || param.point.y > height) {
toolTip1.style.display = 'none';
return;
}
var dateStr = LightweightCharts.isBusinessDay(param.time)
? businessDayToString1(param.time)
: new Date(param.time * 1000).toLocaleDateString();
toolTip1.style.display = 'block';
var price = param.seriesPrices.get(seriesa);
toolTip1.innerHTML = '<div style="color: rgba(0, 120, 255, 0.9)">⬤ Naira</div>' +
'<div style="font-size: 24px; margin: 4px 0px; color: #20262E">' + (Math.round(price * 100) / 100).toFixed(2) + '</div>' +
'<div>' + dateStr + '</div>';
var left = param.point.x;
if (left > width - toolTipWidth1 - toolTipMargin1) {
left = width - toolTipWidth1;
} else if (left < toolTipWidth1 / 2) {
left = priceScaleWidth1;
}
toolTip1.style.left = left + 'px';
toolTip1.style.top = 80 + 'px';
});
Where I guess the issue comes from is the point where I call the loop of the data from the database but I don't know exactly why it's showing the wrong dates.
<?php
foreach($user->historychart as $key=>$value){
if($value->investment_amount != null){
echo '{ time:'.$value->created_at->format('Y-m-d').', value:'. $value->investment_amount.'},';
}
}
?>
I solved this by passing the date into a double quote to turn it into a string.
<?php
foreach($user->historychart as $key=>$value){
if($value->investment_amount != null){
echo '{ time:"'.$value->created_at->format('Y-m-d').'", value:'. $value->investment_amount.'},';
}
}
?>
for me, I change the code from normal date to timestamp date
now = datetime.datetime.now()
timenow = str(now.strftime("%m/%d/%Y %H:%M:%S"))
date = datetime.datetime.strptime(timenow, "%m/%d/%Y %H:%M:%S")
timestamp = datetime.datetime.timestamp(date)
linechart = {
"time" : timestamp,
"value" : row[0]
}
If you get timestamp from Binance for exemple you have to /1000
The 01/01/1970 is the date of the first timestamp 0000000000, it's returned when your timestamp is too big/wrong
I am building a simple line drawing tool using FabricJs and canvas. Using the mouse someone can draw a line that follows grid lines. Like this picture:
My goal is the restrict the line to be only drawn vertically, horizontally and diagonally following the grid. So far the lines don't stick to the grid, the lines can be draw freely on the canvas.
See picture with red lines:
I'm not sure how to implement that part, any help would be appreciated.
Here is what I have so far.
Thank you.
var canvasA = new fabric.Canvas('a', { selection: false });
var grid = 30;
// create grid
//line
var line, isDown;
function createGrid(board) {
for (var i = 0; i < (600 / grid); i++) {
board.add(new fabric.Line([ i * grid, 0, i * grid, 600], { stroke: '#000', selectable: false }));
board.add(new fabric.Line([ 0, i * grid, 600, i * grid], { stroke: '#000', selectable: false }))
}
}
//create grids
createGrid(canvasA);
//handle line drawing
drawLine(canvasA);
function drawLine (board) {
// add objects
board.on('mouse:down', function(o){
isDown = true;
var pointer = board.getPointer(o.e);
var points = [ pointer.x, pointer.y, pointer.x, pointer.y ];
line = new fabric.Line(points, {
strokeWidth: 2,
fill: 'black',
stroke: 'red',
originX: 'center',
originY: 'center',
});
board.add(line);
});
board.on('mouse:move', function(o){
if (!isDown) return;
var pointer = board.getPointer(o.e);
line.set({ x2: pointer.x, y2: pointer.y });
board.renderAll();
});
board.on('mouse:up', function(o){
isDown = false;
});
}
canvas{
background-color: transparent;
margin: 3px;
width: 120px;
height: 240px;
border: 2px solid #ccc;
padding-left: 0;
}
.boards {
display: inline;
padding-left: 0;
}
.boards canvas {
list-style: none;
display: inline-block;
background-color: transparent;
margin: 3px;
width: 120px;
height: 240px;
}
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.js"></script>
<h1>Fabric Js Snap To grid </h1>
<div class="boards">
<canvas id="a" width="120" height="240"></canvas>
You can find vertical lines using this if statement.
if(Math.abs(line.x1 - line.x2) > Math.abs(line.y1 - line.y2))
And on mouse up remove
board.remove(line);
var canvasA = new fabric.Canvas('a', { selection: false });
var grid = 30;
// create grid
//line
var line, isDown;
function createGrid(board) {
for (var i = 0; i < (600 / grid); i++) {
board.add(new fabric.Line([ i * grid, 0, i * grid, 600], { stroke: '#000', selectable: false }));
board.add(new fabric.Line([ 0, i * grid, 600, i * grid], { stroke: '#000', selectable: false }))
}
}
//create grids
createGrid(canvasA);
//handle line drawing
drawLine(canvasA);
function drawLine (board) {
// add objects
board.on('mouse:down', function(o){
isDown = true;
var pointer = board.getPointer(o.e);
var points = [ pointer.x, pointer.y, pointer.x, pointer.y ];
line = new fabric.Line(points, {
strokeWidth: 2,
fill: 'black',
stroke: 'red',
originX: 'center',
originY: 'center',
});
board.add(line);
});
board.on('mouse:move', function(o){
if (!isDown) return;
var pointer = board.getPointer(o.e);
line.set({ x2: pointer.x, y2: pointer.y });
board.renderAll();
});
board.on('mouse:up', function(o){
if(Math.abs(line.x1 - line.x2) > Math.abs(line.y1 - line.y2))
board.remove(line);
isDown = false;
});
}
canvas{
background-color: transparent;
margin: 3px;
width: 120px;
height: 240px;
border: 2px solid #ccc;
padding-left: 0;
}
.boards {
display: inline;
padding-left: 0;
}
.boards canvas {
list-style: none;
display: inline-block;
background-color: transparent;
margin: 3px;
width: 120px;
height: 240px;
}
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.js"></script>
<h1>Fabric Js Snap To grid </h1>
<div class="boards">
<canvas id="a" width="120" height="240"></canvas>
I have a class, which basically draws a 16x30 grid.
Ideally, I would like to be able to execute:
OSD.setCursor(x y);
OSD.print('Text');
and have it position the word Text at position x, y with each character of Text positioned in the correct location in the grid.
Here's what I have so far:
class MAX7456 {
constructor() {
this.items = null;
this.divs = null;
}
begin() {
var ratioH = 16,
ratioW = 30;
var parent = $('<div />', {
class: 'grid',
width: ratioW * 25,
height: ratioH * 18
}).addClass('grid').appendTo('body');
for (var i = 0; i < ratioH; i++) {
for(var p = 0; p < ratioW; p++) {
this.divs = $('<div />', {
width: 25 - 1,
height: 18 - 1
}).appendTo(parent);
this.items = $('<span />', {
width: 25 - 1,
height: 18 - 1,
style: "padding-left: 2px;"
}).appendTo(this.divs);
}
}
}
setCursor(x, y) {
$('div > span:nth-child(2n-1)').text(function (i, txt) {
$(this).append(i)
i++;
//console.log(txt + $(this).next().text());
});
}
print (txt) {
}
}
var OSD = new MAX7456();
OSD.begin(); // create grid
OSD.setCursor(0, 0); // set text at cursor (x, y)
OSD.print("Label 2");
body {
padding: 0;
font-size: 12px;
}
.grid {
border: 1px solid #ccc;
border-width: 1px 0 0 1px;
}
.grid div {
border: 1px solid #ccc;
border-width: 0 1px 1px 0;
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
JSFiddle
Now this may seems not the ideal solution looking for, In that case my apologies. My requirement was to create a tiled grid based on an image so this how i managed to do it.
Total tile count can be vary as you need. (My case 2500 tiles)
When you adjust the image size that will determine what size of the tile can be.
(function($) {
var imagePadding = 0;
var pluginName = "tiles",
defaults = {
x: 2, // tiles in x axis
y: 2, // tiles in y axis
gap: {
x: 1,
y: 1
}
};
function Plugin(elem, options) {
options = $.extend({}, defaults, options);
var $elem = $(elem).wrap("<div class='tiles-wrapper' />"),
width = $elem.outerWidth(),
height = $elem.outerHeight(),
n_tiles = options.x * options.y,
tiles = [];
$elem.parent(".tiles-wrapper").css({
position: "relative",
width: width,
height: height
});
for (var $i = 0; $i < n_tiles; $i++) {
if ($i >= imagePadding) {
tiles.push("<div class='tile' data-id='" + $i + "' data-clipboard-text='" + $i + "'>" + $i + "</div>");
} else {
tiles.push("<div class='tile' data-id='" + $i + "' data-clipboard-text='" + $i + "'></div>");
}
}
var $tiles = $(tiles.join(""));
// Hide original image and insert tiles in DOM
$elem.hide().after($tiles);
// Set backgrounds
$tiles.css({
float: "left",
width: (width / options.x) - (options.gap.x || options.gap),
height: (height / options.y) - (options.gap.y || options.gap),
marginRight: options.gap.x || options.gap,
marginBottom: options.gap.y || options.gap,
backgroundImage: "url(" + $elem[0].src + ")",
lineHeight: (height / options.y) - (options.gap.y || options.gap) + "px",
textAlign: "center"
});
// Adjust position
$tiles.each(function() {
var pos = $(this).position();
this.style.backgroundPosition = -pos.left + "px " + -pos.top + "px";
});
}
$.fn[pluginName] = function(options) {
return this.each(function() {
new Plugin(this, options);
});
};
}(jQuery));
window.onload = function() {
$('#img').tiles({
x: 21.909,
y: 21.909
});
$(".tile").click(function() {
console.log($(this).data("id"));
});
};
.tiles-wrapper {
z-index: 999;
}
.tile:hover {
opacity: .80;
filter: alpha(opacity=80);
background: #fecd1f!important;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<!-- Latest compiled and minified JavaScript -->
<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div class="container">
<div class="row">
<div class="col-md-8">
<div class="banner-head"></div>
<div class="row">
<div class="col-md-12">
<div class="image-holder">
<img id="img" src="data:image/gif;base64,R0lGODlh6ANsAwAAACH5BAAAAAAALAAAAADoA2wDhwAAAAAAMwAAZgAAmQAAzAAA/wArAAArMwArZgArmQArzAAr/wBVAABVMwBVZgBVmQBVzABV/wCAAACAMwCAZgCAmQCAzACA/wCqAACqMwCqZgCqmQCqzACq/wDVAADVMwDVZgDVmQDVzADV/wD/AAD/MwD/ZgD/mQD/zAD//zMAADMAMzMAZjMAmTMAzDMA/zMrADMrMzMrZjMrmTMrzDMr/zNVADNVMzNVZjNVmTNVzDNV/zOAADOAMzOAZjOAmTOAzDOA/zOqADOqMzOqZjOqmTOqzDOq/zPVADPVMzPVZjPVmTPVzDPV/zP/ADP/MzP/ZjP/mTP/zDP//2YAAGYAM2YAZmYAmWYAzGYA/2YrAGYrM2YrZmYrmWYrzGYr/2ZVAGZVM2ZVZmZVmWZVzGZV/2aAAGaAM2aAZmaAmWaAzGaA/2aqAGaqM2aqZmaqmWaqzGaq/2bVAGbVM2bVZmbVmWbVzGbV/2b/AGb/M2b/Zmb/mWb/zGb//5kAAJkAM5kAZpkAmZkAzJkA/5krAJkrM5krZpkrmZkrzJkr/5lVAJlVM5lVZplVmZlVzJlV/5mAAJmAM5mAZpmAmZmAzJmA/5mqAJmqM5mqZpmqmZmqzJmq/5nVAJnVM5nVZpnVmZnVzJnV/5n/AJn/M5n/Zpn/mZn/zJn//8wAAMwAM8wAZswAmcwAzMwA/8wrAMwrM8wrZswrmcwrzMwr/8xVAMxVM8xVZsxVmcxVzMxV/8yAAMyAM8yAZsyAmcyAzMyA/8yqAMyqM8yqZsyqmcyqzMyq/8zVAMzVM8zVZszVmczVzMzV/8z/AMz/M8z/Zsz/mcz/zMz///8AAP8AM/8AZv8Amf8AzP8A//8rAP8rM/8rZv8rmf8rzP8r//9VAP9VM/9VZv9Vmf9VzP9V//+AAP+AM/+AZv+Amf+AzP+A//+qAP+qM/+qZv+qmf+qzP+q///VAP/VM//VZv/Vmf/VzP/V////AP//M///Zv//mf//zP///wAAAAAAAAAAAAAAAAiuAPcJHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXMmypcuXMGPKnEmzps2bOHPq3Mmzp8+fQIMKHUq0qNGjSJMqXcq0qdOnUKNKnUq1qtWrWLNq3cq1q9evYMOKHUu2rNmzaNOqXcu2rdu3cOPKnUu3rt27ePPq3cu3r9+/gAMLHky4sOHDiBMrXsy4sePHkCNLnky5suXLmDNrSN7MubPnz6BDix5NurTp06hTq17NurXr17Bjy55Nu7bt27hz697Nu7fv38CDCx9OvLjx48iTK1/OvLnz59CjS59Ovbr169izaznfzr279+/gw4sfT768+fPo06tfz769+/fw48ufT7++/fv48+vfz7+///8ABijggAQWaOCBCCao4II0DDbo4IMQRijhhBRWaOGFGGao4YYcdujhhyCGKOKIJJZo4okopqjiiiy26OKLMMYo44w01i1o44045qjjjjz26OOPQAYp5JBEFmnkkUgmqeSSTDbp5JNQRinllFRWaeWVWGYpqeWWXHbp5ZdghinmmGSWaeaZaKap5ppstunmm3DGKeecdNZp55145qkn55589unnn4AGKuighBZq6KGIJqrooow26uijkEYq6aSUVmrppZhmI6rpppx26umnoIYq6qiklmrqqaimquqqrLbq6quwxirrrLTWImrrrbjmquuuvPbq66/ABivssMQWa+yxyCar7LLMNuvss9AfRivttNRWa+212Gar7bbcduvtt+CGK+645JZr7rnoph6r7rrstuvuu/DGK++89NZr77345qvvvvz26++/AAcdLPDABBds8MEIJ6zwwgw37PDDEEcs8cQUV2zxxRgbZ6zxxhx37PHHIIcs8sgkl2zyySinrPLKLLfsG/LLMMcs88w012zzzTjnrPPOPPfs889ABy300BlEF2300UgnrfTSTDft9NNQRy311FRXbfXVGVhnrfXWXHft9ddghy322GSXbfbZaKet9toXbLft9ttwxy333HTXbffdeOet99589+0X99+ABy744IQXbvjhiCeu+OKMN+7445AWRy755JRXbvnlmGeu+eacd+7556CHLhf66KSXbvrpqKeu+uqst+7667DHLvvstBXXbvvtuOeu++689+7778AHL/zwxBcVb/zxyCev/PLMN+/889BHL/301FdvFP312Gev/fbcd+/99+CHL/745JdvFP756Kev/vrst+/++/DHL//89NdvFf/9+Oev//789+///wAMoAAHSMACGhXwgAhMoAIXyMAGOvCBEIygBCdIwQoUWvCCGMygBjfIwQ568IMgDKEIR0gVwhKa8IQoTKEKV8jCFrrwhTCMoQxnE0jDGtrwhjjMoQ53yMMe+vCHQAwVohCHSMQiGvGISEyiEpfIxCY68YlQE4yiFKdIxSpa8YpYzKIWt8jFLnoS8YtgDKMYx0jGMprxjGhMoxrXE8jGNrrxjXCMoxznSMc62vGOeMwSox73yMc++vGPgAykIAdJyEIaE/KQiEykIhfJyEY68pGQjKQkJ0kSyUpa8pKYzKQmN8nJTnryk6AMEaUoR0nKUprylKhMpSpXycpWErrylbCMpSxnScta2vKWuMylLhB3ycte+vKXwAymMIdJzGIaEvOYyEymMpfJzGY685nQjKY0pxFJzWpa85rYzKY2t8nNbnrzmxDgDKc4x0nOcprznOhMpzrXEcnOdrrznfCMpzznSc962vOeEPjMpz73yc9++vOfAA2oQAcPStCCGvSgCE2oQhfK0IY6EfShEI2oRCdK0Ypa9KIYzahGDzfK0Y569KMgDalIR0rSkg+a9KQoTalKV8rSlrr0pTAPjalMZ0rTmtr0pjjNqU53D8rTnvr0p0ANqlCHStSiGg/1qEhNqlKXytSmOvWpUI0PqlSnStWqWvWqWM2qVrfKENWuevWrYA2rWMdK1rKa9awNaE2rWtfK1ra69a1wjQ+rXOdK17ra9a54zate98oO17769a+ADaxgB0vYwhoP9rCITaxiF8vYxjr2sZCNDqxkJ0vZylr2spjNrGY3DsvZznr2s6ANrWhHS9rSDpr2tKhNrWpXy9rWuva1DbCNrWxnS9va2va2uM0OrW53y9ve+va3wA2ucIcOS9ziGve4yE2ucpfL3OYMOve50I2udKdL3epaDve62M2udrfL3e5697vgDg2veMdL3vKa97zoTa96DNfL3va6973wja985wxL3/ra9774za9+98sO3/76978ADrCAB0zgAhsN+MAITrCCF8zgBjv4wQwQjrCEJ0zhClv4whgOzrCGN8zhDnv4wyAOsYgQR0ziEpv4xChOsYpX7JqAAAA7"
alt="event picture" />
</div>
</div>
</div>
</div>
</div>
</div>