Forum Moderators: phranque

Message Too Old, No Replies

Cosmetics. trying to decide on a type of chart

         

csdude55

11:52 pm on Jan 25, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I have a 24-hour hourly forecast for my local area, and I'd like to make it nice and pretty for my users. The data I have is temperature, wind chill, and chance of precipitation (all numbers). I could realistically go 167 hours ahead, but I'm sticking with 24.

The width of the area I can use ranges from 300px-ish (98% of the mobile viewport) up to 600px (desktop, using a 320px right column for ads).

I originally considered just making a color coded vertical bar chart, then use the number to create a DIV element with that number for it's height, like so:

<?php
$tempArr = array(
'42',
'39',
'37'
);

$windArr = array(
'40',
'37',
'35'
);

$rainArr = array(
'0',
'0',
'10
);

for ($i = 0; $i < count($tempArr); $i++) {
echo <<<EOF
<div style="float: left; margin-right: 5px">
<div style="float: left; width: 8%; height: {$tempArr[$i]}px; background: red"></div>
<div style="float: left; width: 8%; height: {$windArr[$i]}px; background: orange"></div>
<div style="float: left; width: 8%; height: {$rainArr[$i]}px; background: blue"></div>
</div>

EOF;
}


But on that 300px mobile screen, 72 bars with a space in between for appearance would only be about 3px wide for each bar. And if the entire column is 12px wide (300x24), that doesn't give a lot of room in that column for the text for each of those values.

I could JUST chart the temperature in the same way, then a separate table to show the wind chill and precipitation. Not as pretty, but more functional.

Or I could download a jQuery plugin or something to make a prettier chart, but those tend to be HUGE in file size and would slow the page down a lot. This would be pretty, but I don't really want a 1MB file to load on the page:

[jqueryscript.net...]

What do you guys think? Any other interesting ways to create a chart, or can you suggest a plugin you've used that would do the job well without being a big file size?

csdude55

7:47 am on Jan 26, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Well, I THINK that an SVG > polyline might be the way to go, if I can figure it out. I can't seem to find a lot of information on exactly how to do it beyond the basics.

Here's what I have so far:

<svg width="100%" height="100" style="background: #00AAAA">
<polyline points="0,37 20,39 40,41 60,45 80,44 100,43"
stroke="gold" stroke-width="3"
stroke-linecap="round" fill="none" stroke-linejoin="round" />
</svg>


[jsfiddle.net...]

But there are a million questions!

1. How do I make it scaleable / responsive? If the width of the screen is 300px then obviously I don't want there to be 20px between each point, but I can't find a way to make the points at 8%, 16%, 24%, etc. Ideally, I would like for the element to be 100% of the container; if I have 6 points then I would want them to stretch out to fill up the container, but if I have 24 points then I would want them to be more narrow to fit the container.

2. How can I make the color beneath the polyline change? Like, if I wanted the area above the polyline to be white, the line to be red, and the color beneath the red line to be orange...?

3. I don't understand "viewbox" or "preserveAspectRatio" at all...

NickMNS

2:59 pm on Jan 26, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



1. How do I make it scaleable / responsive?

and
I don't understand "viewbox"

These are basically the same question and its your lucky day because I happened to be searching for a similar answer and have this post from stack exchange that answers it.
[graphicdesign.stackexchange.com...]

That said, you must set either the height or width of the svg otherwise it doesn't display correctly in Safari or IE. For this reason I set the width and height in CSS and then adjust them with @media for specific screen sizes.

3. I don't understand ... "preserveAspectRatio" at all...

There is not much to understand, basically it controls how the SVG is "squeezed" as the image is resized (for example in a responsive design). In most cases you don't want the aspect ratio to change, that is you wouldn't want the circles to appears as ovals. So you don't need to set it or set it to the default value:
preserveAspectRatio="xMidYMid meet"


2. How can I make the color beneath the polyline change?

SVG renders the elements based on the order they appear. So you can add a polygon before the polyline with the same basic shape as the polyline except for two added points where it meets the x axis, and with the fill set the color you want . You could do this for each polyline and have three colors. And you can adjust the opacity of the fill and then you can the see the color change if the lines cross.
demo: [jsfiddle.net...]

How do you intend generating the SVG, on page with JS or server side in PHP?

lammert

8:15 pm on Jan 26, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I had the same problem a few years ago and settled with dygraphs. It is a JS graph library originally developed for internal use at Google but made open source in 2009. It has built-in zoom functions and scales very well on both desktop and mobile. It loads fast, even with thousands of data points in a graph is my experience.

It is fully responsive. When rotating the phone, the graph automatically rescales.

csdude55

4:14 am on Jan 27, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



That's awesome, @NickMNS :-D I think I'm making some progress here. I'm generating via PHP right now, but when I'm done I'll do a speed test to make sure it's not too slow. If it is then I might move it to jQuery, or maybe just move the PHP to a separate file to load via ajax.

I have it scaling properly, which is very cool. But I decided to drop the second polyline, it was too much and confusing... so I'll find a way to chart that separately in text.

I started playing with the "marker" concept, but that wasn't quite getting me where I wanted to be. Then I created a vertical line at each segment that was 100% of the height of the SVG at each point, but I couldn't get the line to go through the polygon so I didn't love that, either. In the end, this is where I think I've settled:

[jsfiddle.net...]

The only thing left... how do I make the area at the bottom (the section with the timestamps) in white instead of blue?

@lammert, I had already spent a lot of time working on the SVG, so I don't really want to abandon it now. I'm going to do a chart on every page of the site to show pageviews, though, so I'll look at that when I start on those charts. Thanks for the tip!

csdude55

4:18 am on Jan 27, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Belay that, I think I figured it out... I set up a 3rd polygon in white:

<polygon points="
0, 130
0, 110
598, 110
598, 130"
fill="#FFF" />


[jsfiddle.net...]

I KNEW I'd figure it out as soon as I posted the question! lol

not2easy

4:28 am on Jan 27, 2020 (gmt 0)

WebmasterWorld Administrator 10+ Year Member Top Contributors Of The Month



That blue is your background color. You could try a linear gradient so the very bottom would be white, not sure how it might affect the look of the data though?

Edited to add: I see you found a better answer, while I was posting.

csdude55

12:10 am on Jan 30, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Hey @NickMNS et al, any clue on how to set a tooltip or title on each marker? I'm about to set up another SVG with 365 markers, so it'd be nice if the user could simply mouse over the segments to see each of the values.

I know how to set a title for the whole SVG:

<svg viewBox="0 0 598 100" xmlns="http://www.w3.org/2000/svg">
<polygon points="
0, 100
0, 56
26, 55
...
598, 100"
stroke="yellow" stroke-width="0"
fill="#FFFFE0">
<title>Yup</title>
</polygon>
</svg>


But I want them to be able to mouse over the segment at 0x and see "56", then mouse over at 26x to see "55", and so on.

I'm using jQuery, if that helps. I started to go down a rabbit hole of creating a bunch of DIV tags to position on top of the polygon, which would technically work, but I really hope there's a better way! With 24 segments it isn't bad, but when I make one with 365 segments that will be a ton of DIV tags.

<style>
#container {
position: relative;
}

#tags {
display: flex;
align-items: stretch;
align-content: stretch;

overflow: hidden;
width: 100%;

position: absolute;
top: 0;
left: 0;
}

#tags div {
/* I'm struggling with the width because I need it to stay scalable. Setting it to 4.34x%works, but it's not perfect. I thought that align-items and align-content would make the DIVs fit by default, but it doesn't */
width: 4.34782608695652%;
height: 100%;
}
</style>

<div id="container">
<svg viewBox="0 0 598 100" xmlns="http://www.w3.org/2000/svg">
<polygon points="
0, 100
0, 56
26, 55
...
598, 100"
stroke="yellow" stroke-width="0"
fill="#FFFFE0">
<title>Yup</title>
</polygon>
</svg>

<div id="tags">
<div title="56"></div>
<div title="55"></div>
<div title="54"></div>
<div title="54"></div>
</div>
</div>

<script>
$('#tags').height($('svg').height());
</script>

NickMNS

3:37 am on Jan 30, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



The quick and dirty solution is to add a title tag to the svg element. Not sure about browser compatibility but it is easy. Otherwise you need to add an event listener for on click or hover and then show something.

csdude55

2:00 pm on Jan 30, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



The title tag will only show the one bit of text, though, right? Not change for each segment?

lammert

2:39 pm on Jan 30, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



It is never too late to switch to the graph library I mentioned earlier which has this functionality to display values when hovering over the graph built-in :)

NickMNS

2:42 pm on Jan 30, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Its is one title for each svg element. You could split your polyline into a series of line segments and a title to each.

To be honest I didn't read through your post in great detail, so I'm not sure what you are trying to do. I will to try to look it at later on today when I get some free time.

NickMNS

7:56 pm on Jan 30, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I looked a little closer at your post.

I can't think of any special solution, it will basically boil down to some variation of my previous post.

Some ideas:
1- Split the polyline into individual line segments (as described in my previous post)
2- Add circles (or other shape) at (above) each vertex of the polyline, use either a small visible circle or a larger one that is transparent. This is similar to using a "marker" but the marker forces you to have the same marker at all vertices and thus the same title text (start and end can be different). You can create one generic circle wrapped in a <g> tag and then use a <use> tag to display it at right location with the right text:

<use href="url(#myDot)" x="20" y="30"><title>Yup!</title></use>
<use href="url(#myDot)" x="24" y="28"><title>Nope!</title></use>

The x and y locations would match each x,y pair used in the points attribute for the polyline. Even if you choose not to use the title tag approach you would still want to take a similar approach. Otherwise you need to track the position of the mouse and when the mouse is at some position x,y (or ideally region) then show message "yup!". But since your graph is changing and the text is changing it would be a rather elaborate script to write. Having a fixed region defined by an element is just much easier to manage.

I hope this helps.

csdude55

10:39 pm on Jan 30, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



@lammert, the jury is still out! LOL I'm getting past the learning curve, but if SVG doesn't do what I want then I'm not so far that I can't change course.

@NickMNS, awesome, man, that's getting me there! Only issue is that they have to mouse over the actual polygon to get the title, so if they mouse over the area above the line then there's nothing. Which makes sense in retrospect, I just haven't decided if it's a deal breaker.

Here's a fiddle, if you want to see (I've abbreviated a lot of it for readability):

[jsfiddle.net...]

Plugging in a Y coordinate doesn't seem to make any difference, although if I have it on the first segment then it seems to make it only work sporadically... not sure what's up with that, but since it doesn't help then I guess I should just leave it off, anyway.

NickMNS

2:12 am on Jan 31, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Here are two approaches rolled up in one fiddle:
[jsfiddle.net...]

First is what I was describing with the circles in my post above. Hovering over each point will reveal the title text. Simple. From a UI perspective the points are ambiguous, instead I would make bars.

So for the second approach I made two polygon bars, if you hover over the bars a popup appear with the text. This uses CSS to hide and display the text on hover. I create a group <g>and give it a class name (.bars in the demo). Inside the group I place the polygon bar followed by the text box, which in turn is another group with a rectangle and text. I give the inner group an ID. In the CSS I set all the ID's to display: none and then set styling as follows:

.bars:hover #myID-1, .bars:hover #myID-2, .bars:hover #myID-3 {
display:block;
}

The .bars:hover is so the style only applies when hovering an element of class .bars. The space followed by the #myID-.., means that the style applies only to the class that has a child of #myID-.. Like this it applies specifically to that class and ID combination and not the others.

Now the astute reader may say "why so complicated, why not simply make the id:hover" The reason is that hover doesn't apply to element set to display none. So by hovering the class, the ID element appears, and goes back to display none when not hovering. The other benefit is that the text box need not be above the hover area.

I hope this helps.

csdude55

5:15 am on Feb 1, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I've spent the day playing around with that, Nick, and I think that your second approach comes closer to what I'm wanting. My only concern is that, with 365 segments, it's going to end up huuuuge! So I'm not quite giving up on using <use ...><title>...</title></use> yet.

I'm only having one real hiccup with it, maybe I don't understand the X and Y coordinates? What's happening is:

1. the TITLE for the segments only works for about half of the vertical length. If I mouse over in the upper half of the polygon, nothing happens;

2. sometimes, the TITLE for a totally different segment shows up! It's weird; I put the mouse at the bottom of the segment and get the right title, then I move the mouse straight up and it changes to a different one. Then I move further up, and it changes to yet another one! So I'm obviously not setting the coordinates correctly.

Here's an abbreviated version of how I'm doing it:

<svg viewBox="0 0 350.961538462 115" xmlns="http://www.w3.org/2000/svg">
<polygon points="
0, 115
0, 24.8113207547
14.0384615385, 21.0377358491
...
350.961538462, 35.7547169811
350.961538462, 115"
id="lines"
fill="#E6F2FA" />

<polyline points="
0, 24.8113207547
14.0384615385, 21.0377358491
...
350.961538462, 35.7547169811"
stroke="#B5D9F0" stroke-width="1"
stroke-linecap="round" fill="none" stroke-linejoin="round" />

<!-- I'm matching X and Y to the X and Y of the respective points -->
<!-- width is 365 / number of segments, height is the max height of the SVG -->
<use href="#lines" x="0" y="24.8113207547" width="14.0384615385" height="115">
<title>$2.39, Friday, Mar 8, 2019</title>
</use>

<use href="#lines" x="14.0384615385" y="21.0377358491" width="14.0384615385" height="115">
<title>$2.49, Tuesday, Apr 2, 2019</title>
</use>

...

<use href="#lines" x="350.961538462" y="35.7547169811" width="14.0384615385" height="115">
<title>$2.10, Thursday, Jan 30, 2020</title>
</use>
</svg>


Here's the fiddle so you can see it in action:

[jsfiddle.net...]

If you hold your mouse near the bottom of the SVG in the middle, for example, it'll show a TITLE of "$2.37, Monday, Jul 29, 2019". Then move the mouse straight up slowly and it'll change to "$2.65, Sunday, Jun 30, 2019". The farther to the right you go on the SVG, the worse it seems to do it.

What value am I plugging in wrong?

NickMNS

2:38 pm on Feb 3, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



So the issue is that you are reusing the full area below the curve as the hover element ("#lines"). You are simply copying that shape over every 14 pixels. The result is not what you expect. See the fiddle below. I removed the fill from the "polyline" element and added different fill colors back in the first few "use" elements. This allows you to clearly see the result.
[jsfiddle.net...]


To keep it simple, I added a rectangle with the id #lines in "defs" tags. The "defs" tags prevent the rectangle from appearing. The "use" elements remain unchanged. See the result here:
[jsfiddle.net...]

You could make the rectangle a polygon, but then you would need one for each day. Or you can simply set the "y" attribute 0 and let the rectangle span the full height. Two side notes, one I added a stroke to the rectangle, resulting in vertical lines. Two, I set the opacity to 0.1 so that the rectangle is transparent but still can be seen, you can dial that down to zero and then not need to worry about the first point.