Skip to content

Commit

Permalink
Fix transition bug, extract text to component
Browse files Browse the repository at this point in the history
  • Loading branch information
onlyskin committed Nov 26, 2023
1 parent fc0d780 commit 69ca78e
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 68 deletions.
4 changes: 2 additions & 2 deletions css/pokemon-types-d3.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ svg circle:hover {
stroke-width: 3px;
}

.focused {
#focused-text {
font-family: 'Open Sans Condensed', sans-serif;
font-size: 10vw;
text-anchor: middle;
alignment-baseline: middle;
user-select: none;
}

.title {
#title-text {
font-family: 'Open Sans Condensed', sans-serif;
font-size: 4vw;
text-anchor: middle;
Expand Down
52 changes: 50 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import stream from 'mithril/stream';
import * as d3 from 'd3';
import { PokemonType, INode } from './type_to_nodes';
import { updateVisualisation } from './update_visualisation';
import { IState } from './utils';
import { boundingDimensions, IState } from './utils';
import { forceSimulation } from './simulation';

const state: IState = {
activeTransition: false,
setActiveTransition: function(is_active: boolean) {
this.activeTransition = is_active;
},
focusedType: stream<PokemonType>('fire'),
hoveredNode: stream<INode | undefined>(undefined),
setFocusedType: function(newType: PokemonType) {
Expand All @@ -18,7 +22,7 @@ const state: IState = {
m.redraw();
},
setHoveredNode: function(newNode?: INode) {
if (newNode === this.hoveredNode()) {
if (newNode === this.hoveredNode() || this.activeTransition) {
return;
}

Expand All @@ -27,6 +31,28 @@ const state: IState = {
}
};

function visualisationTitle(state: IState): string {
const hovered = state.hoveredNode();
const focused = state.focusedType();

if (hovered === undefined) {
return '';
}

let attacking;
let defending;

if (hovered.direction === 'from') {
attacking = hovered.name;
defending = focused;
} else {
attacking = focused;
defending = hovered.name;
}

return `${attacking} gets ${hovered.multiplier}x against ${defending}`;
}

const Visualisation: m.Component<{
simulation: d3.Simulation<INode, undefined>,
state: IState,
Expand All @@ -36,11 +62,31 @@ const Visualisation: m.Component<{
oncreate: function({attrs: {simulation, state}, dom}) {
updateVisualisation(dom, simulation, true, state);
this.oldFocused = state.focusedType();
this.domComputations(state, dom);
},
onupdate: function({attrs: {simulation, state}, dom}) {
const focusedUpdated = state.focusedType() !== this.oldFocused;
updateVisualisation(dom, simulation, focusedUpdated, state);
this.oldFocused = state.focusedType();
this.domComputations(state, dom);
},
domComputations: function(state: IState, dom: Element) {
this.updateFocusedText(state, dom);
this.updateTitle(state, dom);
},
updateFocusedText: function(state: IState, dom: Element) {
const {height, width} = boundingDimensions(dom);
const el = dom.querySelector('#focused-text');
el.setAttribute('x', (width * 0.5).toString());
el.setAttribute('y', (height * 0.5).toString());
el.textContent = state.focusedType();
},
updateTitle: function(state: IState, dom: Element) {
const {height, width} = boundingDimensions(dom);
const el = dom.querySelector('#title-text');
el.setAttribute('x', (width * 0.5).toString());
el.setAttribute('y', (height * 0.125).toString());
el.textContent = visualisationTitle(state);
},
view: () => {
return m(
Expand All @@ -49,6 +95,8 @@ const Visualisation: m.Component<{
version: '1',
xmlns: 'http://www.w3.org/2000/svg',
},
m('text#focused-text', {}, 'bug'),
m('text#title-text', {}, 'bug'),
);
},
};
Expand Down
73 changes: 9 additions & 64 deletions src/update_visualisation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as d3 from 'd3';
import { Pokedex } from 'pokeapi-js-wrapper';
import { PokemonType, INode } from './type_to_nodes';
import { INode } from './type_to_nodes';
import { boundingDimensions, nodeRadius, IState } from './utils';
import type_to_nodes from './type_to_nodes';
import { tick } from './simulation';
Expand Down Expand Up @@ -31,8 +31,6 @@ export async function updateVisualisation(
svg.setAttribute('width', width.toString());
svg.setAttribute('height', height.toString());

updateTitle(svg, state);

if (focusedUpdated) {
const response = await pokedex.getTypeByName(state.focusedType());
const nodes: INode[] = type_to_nodes(response);
Expand All @@ -43,65 +41,6 @@ export async function updateVisualisation(
}

updateCircles(svg, simulation, state);
updateFocused(svg, state.focusedType());
}


function updateFocused(svg: Element, focused: PokemonType): void {
updateTextElement(svg, (focused as string), 'focused', 0.5, 0.5);
}

export function visualisationTitle(state: IState): string {
const hovered = state.hoveredNode();
const focused = state.focusedType();

if (hovered === undefined) {
return '';
}

let attacking;
let defending;

if (hovered.direction === 'from') {
attacking = hovered.name;
defending = focused;
} else {
attacking = focused;
defending = hovered.name;
}

return `${attacking} gets ${hovered.multiplier}x against ${defending}`;
}

function updateTitle(svg: Element, state: IState): void {
updateTextElement(svg, visualisationTitle(state), 'title', 0.5, 0.125);
}

function updateTextElement(
svg: Element,
text: string,
className: string,
xMultiple: number,
yMultiple: number,
): void {
const { width, height } = boundingDimensions(svg);

const updating = d3.select(svg)
.selectAll<Element, string>(`.${className}`)
.data<string>([text], d => (d as string));

updating
.enter()
.append<Element>('text')
.merge(updating)
.classed(className, true)
.attr('x', width * xMultiple)
.attr('y', height * yMultiple)
.text(id);

updating
.exit()
.remove();
}

function updateCircles(
Expand All @@ -110,8 +49,14 @@ function updateCircles(
state: IState,
): void {
const { width, height } = boundingDimensions(svg);
const nodeTransition = d3.transition()
.duration(600);
const nodeTransition = d3.transition('circles')
.duration(600)
.on('start', () => {
state.setActiveTransition(true);
})
.on('end', () => {
state.setActiveTransition(false);
});

const updatingNodes = d3.select(svg)
.selectAll<Element, INode>('circle')
Expand Down
2 changes: 2 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { PokemonType, INode } from './type_to_nodes';
export interface IState {
focusedType: () => PokemonType;
hoveredNode: () => INode | undefined;
activeTransition: boolean;
setFocusedType: (newType: PokemonType) => undefined;
setHoveredNode: (newNode?: INode) => undefined;
setActiveTransition: (isActive: boolean) => undefined;
}

const NODE_SIZE_FACTOR = 0.03;
Expand Down

0 comments on commit 69ca78e

Please sign in to comment.