polarization data visualized
Этот коммит содержится в:
родитель
06ef9c3806
Коммит
d8d77a137a
@ -141,6 +141,36 @@ a:hover, span.pseudolink:hover {
|
|||||||
background-color: #e24545a1;
|
background-color: #e24545a1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* polarization */
|
||||||
|
.pol-l {
|
||||||
|
background-color: #2e64a0;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pol-ll {
|
||||||
|
background-color: #61a3de;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pol-c {
|
||||||
|
background-color: #96659e;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pol-lr {
|
||||||
|
background-color: #a15552;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pol-r {
|
||||||
|
background-color: #ca0800;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pol-undef {
|
||||||
|
background-color: #e0d3e2;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
/* the landing page */
|
/* the landing page */
|
||||||
.page-wrapper {
|
.page-wrapper {
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
import { width, panelHeight, controlsHeight } from '../stores/dimensions';
|
import { width, panelHeight, controlsHeight } from '../stores/dimensions';
|
||||||
import { tooltip } from '../stores/eventSelections';
|
import { tooltip } from '../stores/eventSelections';
|
||||||
import { fade, slide } from 'svelte/transition';
|
import { fade, slide } from 'svelte/transition';
|
||||||
import { timeFormat, format } from 'd3';
|
import { timeFormat } from 'd3';
|
||||||
import { extractHostname } from '../utils/misc';
|
import { extractHostname } from '../utils/misc';
|
||||||
import {
|
import {
|
||||||
platformFilter,
|
platformFilter,
|
||||||
@ -20,6 +20,8 @@
|
|||||||
import EventTooltipCross from './EventTooltipCross.svelte';
|
import EventTooltipCross from './EventTooltipCross.svelte';
|
||||||
import ScoreBar from './ScoreBar.svelte';
|
import ScoreBar from './ScoreBar.svelte';
|
||||||
import ScoreQuestions from './ScoreQuestions.svelte';
|
import ScoreQuestions from './ScoreQuestions.svelte';
|
||||||
|
import ImpactStrip from './ImpactStrip.svelte';
|
||||||
|
import PolarizationLegend from './PolarizationLegend.svelte';
|
||||||
import Share from './Share.svelte';
|
import Share from './Share.svelte';
|
||||||
|
|
||||||
const offset = {
|
const offset = {
|
||||||
@ -32,7 +34,6 @@
|
|||||||
|
|
||||||
const attributionTf = timeFormat('%B %d, %Y');
|
const attributionTf = timeFormat('%B %d, %Y');
|
||||||
const activityTf = timeFormat('%B %Y');
|
const activityTf = timeFormat('%B %Y');
|
||||||
const commaFormat = format(',');
|
|
||||||
|
|
||||||
let elem;
|
let elem;
|
||||||
let tWidth, tHeight;
|
let tWidth, tHeight;
|
||||||
@ -181,19 +182,19 @@
|
|||||||
<p>pending</p>
|
<p>pending</p>
|
||||||
{:else}
|
{:else}
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<ImpactStrip value={$tooltip.tp.smiFacebook}
|
||||||
<span class="smi-score facebook">{commaFormat($tooltip.tp.smiFacebook)}</span>
|
polarization={$tooltip.tp.polarization}
|
||||||
<span class="smi-label">Facebook</span>
|
label="Facebook" />
|
||||||
</li>
|
<ImpactStrip value={$tooltip.tp.smiTwitter}
|
||||||
<li>
|
polarization={$tooltip.tp.polarization}
|
||||||
<span class="smi-score twitter">{commaFormat($tooltip.tp.smiTwitter)}</span>
|
label="Twitter" />
|
||||||
<span class="smi-label">Twitter</span>
|
<ImpactStrip value={$tooltip.tp.smiReddit}
|
||||||
</li>
|
polarization={$tooltip.tp.polarization}
|
||||||
<li>
|
label="Reddit" />
|
||||||
<span class="smi-score reddit">{commaFormat($tooltip.tp.smiReddit)}</span>
|
|
||||||
<span class="smi-label">Reddit</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
{#if ($tooltip.tp.polarization.fulfills10Articles || $tooltip.tp.polarization.fulfills25Percent)}
|
||||||
|
<PolarizationLegend />
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if ($tooltip.tp.imageUrl)}
|
{#if ($tooltip.tp.imageUrl)}
|
||||||
@ -426,26 +427,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.smi li {
|
|
||||||
margin: 0.2rem 0.3rem 0.2rem 0;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smi-score {
|
|
||||||
padding: 0 0.2rem;
|
|
||||||
border: none;
|
|
||||||
border-radius: 3px;
|
|
||||||
box-shadow: 0 1px 2px rgba(0,0,0,0.07),
|
|
||||||
0 2px 4px rgba(0,0,0,0.07);
|
|
||||||
}
|
|
||||||
|
|
||||||
.smi-label {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0 0.1rem;
|
|
||||||
border: none;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
@ -458,11 +439,6 @@
|
|||||||
font-size: 0.6rem;
|
font-size: 0.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-break {
|
|
||||||
word-break: keep-all;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-wrapper .image {
|
.scroll-wrapper .image {
|
||||||
min-height: 1%;
|
min-height: 1%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
52
src/components/ImpactStrip.svelte
Обычный файл
52
src/components/ImpactStrip.svelte
Обычный файл
@ -0,0 +1,52 @@
|
|||||||
|
<script>
|
||||||
|
import { format } from 'd3';
|
||||||
|
|
||||||
|
import PolarizationStrip from './PolarizationStrip.svelte';
|
||||||
|
|
||||||
|
export let value = 0;
|
||||||
|
export let polarization;
|
||||||
|
export let label = '';
|
||||||
|
|
||||||
|
const commaFormat = format(',');
|
||||||
|
|
||||||
|
let valueWidth = 0;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<div class="smi-score {label.toLowerCase()}"
|
||||||
|
bind:clientWidth={valueWidth}>
|
||||||
|
{commaFormat(value)}
|
||||||
|
</div>
|
||||||
|
<span class="smi-label">
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
{#if ((polarization.fulfills10Articles || polarization.fulfills25Percent) && value > 0)}
|
||||||
|
<PolarizationStrip polarization={polarization[label.toLowerCase()]}
|
||||||
|
smi={value}
|
||||||
|
valueWidth={valueWidth} />
|
||||||
|
{/if}
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
li {
|
||||||
|
margin: 0.2rem 0.3rem 0.2rem 0;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
min-width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smi-score {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 0.2rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.07),
|
||||||
|
0 2px 4px rgba(0, 0, 0, 0.07);
|
||||||
|
}
|
||||||
|
|
||||||
|
.smi-label {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 0.1rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
52
src/components/PolarizationLegend.svelte
Обычный файл
52
src/components/PolarizationLegend.svelte
Обычный файл
@ -0,0 +1,52 @@
|
|||||||
|
<script>
|
||||||
|
import { categories } from '../inputs/polarization';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="pol-legend">
|
||||||
|
<p>Polarization:</p>
|
||||||
|
<ul>
|
||||||
|
{#each categories as category (category)}
|
||||||
|
<li>
|
||||||
|
<div class="pol-legend-field pol-{category.id}"></div>
|
||||||
|
<p>{category.name}</p>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.pol-legend {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-family: var(--font-02);
|
||||||
|
color: var(--dfrlab-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pol-legend p {
|
||||||
|
margin-right: 0.7rem;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pol-legend ul {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pol-legend ul li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pol-legend ul li p {
|
||||||
|
font-size: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pol-legend-field {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
margin-right: 0.2rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
108
src/components/PolarizationStrip.svelte
Обычный файл
108
src/components/PolarizationStrip.svelte
Обычный файл
@ -0,0 +1,108 @@
|
|||||||
|
<script>
|
||||||
|
import { categories } from '../inputs/polarization';
|
||||||
|
|
||||||
|
import { scaleLinear, line as d3line, curveBasis } from 'd3';
|
||||||
|
|
||||||
|
export let polarization;
|
||||||
|
export let smi = 0;
|
||||||
|
export let valueWidth = 0;
|
||||||
|
|
||||||
|
const magnifierHeight = 20;
|
||||||
|
|
||||||
|
const margin = {
|
||||||
|
top: 4,
|
||||||
|
right: 2,
|
||||||
|
bottom: 0,
|
||||||
|
left: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
const yScale = scaleLinear()
|
||||||
|
.domain([0, 1])
|
||||||
|
.range([margin.top, magnifierHeight - margin.bottom]);
|
||||||
|
|
||||||
|
const line = d3line()
|
||||||
|
.x((d) => d[0])
|
||||||
|
.y((d) => yScale(d[1]))
|
||||||
|
.curve(curveBasis);
|
||||||
|
|
||||||
|
let width;
|
||||||
|
let stack = {};
|
||||||
|
|
||||||
|
$: totalEngagement = Object.keys(polarization).map((k) => polarization[k]).reduce((acc, cur) => acc + cur);
|
||||||
|
$: engagementExplained = totalEngagement / smi;
|
||||||
|
|
||||||
|
$: Object.keys(polarization).forEach((k) => {
|
||||||
|
const value = polarization[k];
|
||||||
|
stack[k] = {
|
||||||
|
value,
|
||||||
|
width: Math.floor(100 * value / totalEngagement, 2)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
$: leftPathData = [
|
||||||
|
[margin.left + valueWidth / 2 - engagementExplained * valueWidth / 2, 0],
|
||||||
|
[margin.left + valueWidth / 2 - engagementExplained * valueWidth / 2, 0.3],
|
||||||
|
[margin.left, 0.7],
|
||||||
|
[margin.left, 1]
|
||||||
|
];
|
||||||
|
|
||||||
|
$: rightPathData = [
|
||||||
|
[valueWidth / 2 + engagementExplained * valueWidth / 2 - margin.right, 0],
|
||||||
|
[valueWidth / 2 + engagementExplained * valueWidth / 2 - margin.right, 0.3],
|
||||||
|
[width * 0.98 - margin.right, 0.7],
|
||||||
|
[width * 0.98 - margin.right, 1]
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="polarization-strip"
|
||||||
|
bind:clientWidth={width}>
|
||||||
|
<svg class="pol-magnifier"
|
||||||
|
width={width}
|
||||||
|
height={magnifierHeight}>
|
||||||
|
<path d={line(leftPathData)} />
|
||||||
|
<path d={line(rightPathData)} />
|
||||||
|
<text x={valueWidth / 2}
|
||||||
|
y={yScale(1)}>
|
||||||
|
{Math.round(engagementExplained * 100)}%
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
<div class="pol-layer-wrapper">
|
||||||
|
{#each categories as category (category.id)}
|
||||||
|
<div class="pol-layer pol-{category.id}"
|
||||||
|
style="width: {stack[category.id].width}%;">
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.polarization-strip {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
path {
|
||||||
|
stroke: var(--dfrlab-gray);
|
||||||
|
stroke-width: 1.5;
|
||||||
|
stroke-dasharray: 2 3;
|
||||||
|
stroke-linecap: round;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-family: var(--font-02);
|
||||||
|
font-size: 0.6rem;
|
||||||
|
text-anchor: middle;
|
||||||
|
fill: var(--dfrlab-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pol-layer-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 1rem;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pol-layer {
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
22
src/inputs/polarization.js
Обычный файл
22
src/inputs/polarization.js
Обычный файл
@ -0,0 +1,22 @@
|
|||||||
|
export const categories = [
|
||||||
|
{
|
||||||
|
id: 'l',
|
||||||
|
name: 'left'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'll',
|
||||||
|
name: 'leanleft'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'c',
|
||||||
|
name: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'lr',
|
||||||
|
name: 'leanright'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'r',
|
||||||
|
name: 'right'
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -92,21 +92,21 @@ const loadData = async () => {
|
|||||||
lr: +d.allsides_engagments_leanright,
|
lr: +d.allsides_engagments_leanright,
|
||||||
r: +d.allsides_engagments_right
|
r: +d.allsides_engagments_right
|
||||||
},
|
},
|
||||||
fb: {
|
facebook: {
|
||||||
l: +d.allsides_engagments_left_facebook,
|
l: +d.allsides_engagments_left_facebook,
|
||||||
ll: +d.allsides_engagments_leanleft_facebook,
|
ll: +d.allsides_engagments_leanleft_facebook,
|
||||||
c: +d.allsides_engagments_center_facebook,
|
c: +d.allsides_engagments_center_facebook,
|
||||||
lr: +d.allsides_engagments_leanright_facebook,
|
lr: +d.allsides_engagments_leanright_facebook,
|
||||||
r: +d.allsides_engagments_right_facebook
|
r: +d.allsides_engagments_right_facebook
|
||||||
},
|
},
|
||||||
tw: {
|
twitter: {
|
||||||
l: +d.allsides_engagments_left_twitter,
|
l: +d.allsides_engagments_left_twitter,
|
||||||
ll: +d.allsides_engagments_leanleft_twitter,
|
ll: +d.allsides_engagments_leanleft_twitter,
|
||||||
c: +d.allsides_engagments_center_twitter,
|
c: +d.allsides_engagments_center_twitter,
|
||||||
lr: +d.allsides_engagments_leanright_twitter,
|
lr: +d.allsides_engagments_leanright_twitter,
|
||||||
r: +d.allsides_engagments_right_twitter
|
r: +d.allsides_engagments_right_twitter
|
||||||
},
|
},
|
||||||
re: {
|
reddit: {
|
||||||
l: +d.allsides_engagments_left_reddit,
|
l: +d.allsides_engagments_left_reddit,
|
||||||
ll: +d.allsides_engagments_leanleft_reddit,
|
ll: +d.allsides_engagments_leanleft_reddit,
|
||||||
c: +d.allsides_engagments_center_reddit,
|
c: +d.allsides_engagments_center_reddit,
|
||||||
|
|||||||
Загрузка…
x
Ссылка в новой задаче
Block a user