works for v2
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="playerChart" ref="container">
|
||||
<!-- <Scatter :data="chart" :options="chartOptions" /> -->
|
||||
<Scatter :data="chart" :options="chartOptions" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -11,24 +12,34 @@ import {
|
||||
PointElement,
|
||||
LineElement,
|
||||
Tooltip,
|
||||
Legend
|
||||
Legend,
|
||||
CoreChartOptions,
|
||||
PluginChartOptions,
|
||||
ScaleChartOptions
|
||||
} from 'chart.js'
|
||||
import annotationPlugin from 'chartjs-plugin-annotation'
|
||||
import dataLabel from 'chartjs-plugin-datalabels'
|
||||
import { Store, useStore } from 'vuex'
|
||||
import { Player, Weapon } from '@/store/index'
|
||||
import { useKillStore, Player, Filters } from '@/stores/kill'
|
||||
|
||||
import { Scatter } from 'vue-chartjs'
|
||||
import { defineComponent } from 'vue'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { ChartEvent } from 'chart.js/dist/core/core.plugins'
|
||||
import { _DeepPartialObject } from 'chart.js/dist/types/utils'
|
||||
|
||||
ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend, annotationPlugin, dataLabel)
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PlayerChart',
|
||||
props: {
|
||||
filters: Object,
|
||||
filters: Object as PropType<Filters>,
|
||||
playerHighlighted: String
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
store: useKillStore(),
|
||||
refreshColors: 0
|
||||
}
|
||||
},
|
||||
emits: ['highlightPlayer'],
|
||||
components: {
|
||||
Scatter
|
||||
@@ -54,7 +65,7 @@ export default defineComponent({
|
||||
return colors
|
||||
},
|
||||
chart () {
|
||||
const chartData = {
|
||||
return {
|
||||
datasets: [
|
||||
{
|
||||
label: 'Players',
|
||||
@@ -62,29 +73,33 @@ export default defineComponent({
|
||||
borderColor: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? this.colors.orange : this.colors.cyan,
|
||||
backgroundColor: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? this.colors.orange : this.colors.cyan,
|
||||
pointRadius: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 20 : 1,
|
||||
pointStyle: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 'crossRot' : 'dot',
|
||||
pointStyle: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 'crossRot' : 'circle',
|
||||
hoverRadius: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 30 : 4,
|
||||
data: [] as { x: number, y: number }[]
|
||||
data: this.playerIdList.map((id) => ({ y: this.players[id].kills, x: this.players[id].deaths }))
|
||||
}
|
||||
]
|
||||
}
|
||||
if (!this.players) {
|
||||
return chartData
|
||||
}
|
||||
chartData.datasets[0].data = this.playerIdList.map(e => {
|
||||
if (!this.players) {
|
||||
return { x: 0, y: 0 }
|
||||
}
|
||||
return { x: this.players[e].deaths, y: this.players[e].kills }
|
||||
})
|
||||
return chartData
|
||||
},
|
||||
chartOptions () {
|
||||
chartOptions (): _DeepPartialObject<CoreChartOptions<'scatter'> & PluginChartOptions<'scatter'> & ScaleChartOptions<'scatter'>> {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
this.$props.playerHighlighted
|
||||
const options = {
|
||||
let endX = 0
|
||||
let endY = 0
|
||||
if (this.players) {
|
||||
const maxX = Math.max(...Object.values(this.players).map(e => e.deaths))
|
||||
const maxY = Math.max(...Object.values(this.players).map(e => e.kills))
|
||||
|
||||
if (maxY > maxX) {
|
||||
endX = maxX
|
||||
endY = (maxX / maxY) * maxY
|
||||
} else {
|
||||
endY = maxY
|
||||
endX = (maxY / maxX) * maxX
|
||||
}
|
||||
}
|
||||
return {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: { duration: 500 },
|
||||
layout: { autoPadding: false },
|
||||
scales: {
|
||||
@@ -124,7 +139,20 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
},
|
||||
annotation: {},
|
||||
annotation: {
|
||||
annotations: {
|
||||
line: {
|
||||
type: 'line',
|
||||
yMin: 0,
|
||||
xMin: 0,
|
||||
yMax: endY,
|
||||
xMax: endX,
|
||||
borderWidth: 2,
|
||||
borderColor: this.colors.orange,
|
||||
borderDash: [1, 5]
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: { display: false },
|
||||
datalabels: {
|
||||
formatter: (value: any, context: any) => {
|
||||
@@ -135,13 +163,13 @@ export default defineComponent({
|
||||
borderWidth: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? 1 : 0,
|
||||
borderRadius: 5,
|
||||
display: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? 'true' : 'auto',
|
||||
align: '-45',
|
||||
align: -45,
|
||||
anchor: 'end',
|
||||
clamp: true,
|
||||
color: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? this.colors.bg : this.colors.fg
|
||||
}
|
||||
},
|
||||
onClick: (e: Event, element: any) => {
|
||||
onClick: (e: ChartEvent, element: any) => {
|
||||
this.$emit('highlightPlayer', element.length > 0 ? this.playerIdList[element[0].index] : undefined)
|
||||
},
|
||||
onHover: (e: any, element: any) => {
|
||||
@@ -149,44 +177,23 @@ export default defineComponent({
|
||||
(e.native.target as HTMLElement).style.cursor = element[0] ? 'pointer' : 'default'
|
||||
}
|
||||
}
|
||||
if (!this.players) {
|
||||
return options
|
||||
}
|
||||
const maxX = Math.max(...Object.values(this.players).map(e => e.deaths))
|
||||
const maxY = Math.max(...Object.values(this.players).map(e => e.kills))
|
||||
let endX, endY
|
||||
if (maxY > maxX) {
|
||||
endX = maxX
|
||||
endY = (maxX / maxY) * maxY
|
||||
} else {
|
||||
endY = maxY
|
||||
endX = (maxY / maxX) * maxX
|
||||
}
|
||||
options.plugins.annotation = {
|
||||
annotations: {
|
||||
line: {
|
||||
type: 'line',
|
||||
yMin: 0,
|
||||
xMin: 0,
|
||||
yMax: endY,
|
||||
xMax: endX,
|
||||
borderWidth: 2,
|
||||
borderColor: this.colors.orange,
|
||||
borderDash: [1, 5]
|
||||
}
|
||||
}
|
||||
}
|
||||
return options
|
||||
},
|
||||
players (): { [key: string]: Player } { return this.store.getters.getPlayerList(this.filters) || {} },
|
||||
players (): { [key: string]: Player } {
|
||||
const data = this.store.getPlayerList(this.filters || {})?.data
|
||||
if (!data) return {}
|
||||
const cut = Object.entries(data).sort((a, b) => {
|
||||
if (a[1].kills < b[1].kills) {
|
||||
return 1
|
||||
}
|
||||
if (a[1].kills > b[1].kills) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}).slice(0, 200)
|
||||
return Object.fromEntries(cut)
|
||||
},
|
||||
playerIdList (): string[] { return Object.keys(this.players) }
|
||||
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
store: useStore(),
|
||||
refreshColors: 0
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
<span v-on:click="sortPlayerList('kills')" :class="sortingData.argument == 'kills' ? 'selected' : ''">K</span>
|
||||
<span v-on:click="sortPlayerList('deaths')" :class="sortingData.argument == 'deaths' ? 'selected' : ''">D</span>
|
||||
<span v-on:click="sortPlayerList('k/d')" :class="sortingData.argument == 'k/d' ? 'selected' : ''">K/D</span>
|
||||
<span v-on:click="sortPlayerList('max_kill_distance')"
|
||||
:class="sortingData.argument == 'max_kill_distance' ? 'selected' : ''">max distance</span>
|
||||
<span v-on:click="sortPlayerList('avg_kill_distance')"
|
||||
:class="sortingData.argument == 'avg_kill_distance' ? 'selected' : ''">average distance</span>
|
||||
<span v-on:click="sortPlayerList('max_distance')"
|
||||
:class="sortingData.argument == 'max_distance' ? 'selected' : ''">max distance</span>
|
||||
<span v-on:click="sortPlayerList('avg_distance')"
|
||||
:class="sortingData.argument == 'avg_distance' ? 'selected' : ''">average distance</span>
|
||||
</div>
|
||||
<div :class="'playerRow ' + (playerId === $props.playerHighlighted ? 'selected' : '')"
|
||||
v-for="(playerId, index) in playerIdList" v-bind:key="playerId" v-on:click="$emit('highlightPlayer', playerId)"
|
||||
@@ -20,32 +20,34 @@
|
||||
<div><span>{{ players[playerId].kills }}</span></div>
|
||||
<div><span>{{ players[playerId].deaths }}</span></div>
|
||||
<div><span>{{ Math.round(players[playerId].kills / Math.max(1, players[playerId].deaths) * 100) / 100 }}</span></div>
|
||||
<div><span>{{ players[playerId].max_kill_distance }}</span></div>
|
||||
<div><span>{{ Math.round(players[playerId].avg_kill_distance * 100) / 100 }}</span></div>
|
||||
<div><span>{{ players[playerId].max_distance }}</span></div>
|
||||
<div><span>{{ Math.round(((players[playerId].total_distance / players[playerId].kills) || 0) * 100) / 100 }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { Player } from '@/store/index'
|
||||
import { useStore } from 'vuex'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { useKillStore, Player, Filters } from '@/stores/kill'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
filters: Object,
|
||||
filters: { type: Object as PropType<Filters>, default: () => ({}) },
|
||||
playerHighlighted: String
|
||||
},
|
||||
emits: ['highlightPlayer'],
|
||||
data () {
|
||||
return {
|
||||
sortingData: { direction: -1, argument: 'kills' as keyof Player | 'k/d' },
|
||||
sortingData: { direction: -1, argument: 'kills' as keyof Player | 'k/d' | 'avg_distance' },
|
||||
playerIdList: [] as string[],
|
||||
store: useStore()
|
||||
store: useKillStore()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
players (): { [key: string]: Player } { return this.store.getters.getPlayerList(this.filters) }
|
||||
players (): { [key: string]: Player } {
|
||||
const { player: _, ...withoutPlayer } = this.filters
|
||||
return this.store.getPlayerList(withoutPlayer)?.data || {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
players: {
|
||||
@@ -55,12 +57,13 @@ export default defineComponent({
|
||||
this.sortingData.direction *= -1
|
||||
this.playerIdList = Object.keys(newval)
|
||||
this.sortPlayerList(this.sortingData.argument)
|
||||
this.playerIdList = this.playerIdList.slice(0, 500)
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
playerHighlighted (newval) {
|
||||
const newElement = this.$refs['player:' + newval] as HTMLElement[]
|
||||
newElement[0].scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
if (newElement && newElement[0]) { newElement[0].scrollIntoView({ behavior: 'smooth', block: 'center' }) }
|
||||
}
|
||||
},
|
||||
updated () {
|
||||
@@ -69,7 +72,7 @@ export default defineComponent({
|
||||
element[0].scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
},
|
||||
methods: {
|
||||
sortPlayerList (arg: ((keyof Player) | 'k/d')) {
|
||||
sortPlayerList (arg: ((keyof Player) | 'k/d' | 'avg_distance')) {
|
||||
if (arg === this.sortingData.argument) {
|
||||
this.sortingData.direction *= -1
|
||||
} else {
|
||||
@@ -87,11 +90,14 @@ export default defineComponent({
|
||||
if (arg === 'k/d') {
|
||||
varA = this.players[a].kills / Math.max(1, this.players[a].deaths)
|
||||
varB = this.players[b].kills / Math.max(1, this.players[b].deaths)
|
||||
} else if (arg === 'avg_distance') {
|
||||
varA = (this.players[a].total_distance / this.players[a].kills) || 0
|
||||
varB = (this.players[b].total_distance / this.players[b].kills) || 0
|
||||
} else {
|
||||
varA = this.players[a][arg]
|
||||
varB = this.players[b][arg]
|
||||
}
|
||||
|
||||
if (varA === undefined || varB === undefined) return 0
|
||||
if (varA < varB) {
|
||||
return -1 * this.sortingData.direction
|
||||
}
|
||||
@@ -136,13 +142,14 @@ export default defineComponent({
|
||||
margin-right: 1rem;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.playerRow,
|
||||
.playerHeaders {
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
grid-template-columns: 4ch 20ch 6ch 6ch 6ch 10ch 1fr;
|
||||
grid-template-columns: 5ch 20ch 6ch 6ch 6ch 10ch 1fr;
|
||||
background: var(--bg-color);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
@@ -201,7 +208,7 @@ export default defineComponent({
|
||||
|
||||
.playerRow,
|
||||
.playerHeaders {
|
||||
grid-template-columns: 4ch 20ch 6ch 6ch 1fr;
|
||||
grid-template-columns: 5ch 20ch 6ch 6ch 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,28 +5,18 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
ArcElement,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Tooltip,
|
||||
Legend
|
||||
} from 'chart.js'
|
||||
import annotationPlugin from 'chartjs-plugin-annotation'
|
||||
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
|
||||
import dataLabel from 'chartjs-plugin-datalabels'
|
||||
import { Store, useStore } from 'vuex'
|
||||
import { Player, Weapon } from '@/store/index'
|
||||
import { Weapon, Filters, useKillStore } from '@/stores/kill'
|
||||
import { Doughnut } from 'vue-chartjs'
|
||||
import { defineComponent } from 'vue'
|
||||
import { functionExpression } from '@babel/types'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
|
||||
ChartJS.register(ArcElement, Tooltip, Legend, dataLabel)
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PlayerChart',
|
||||
props: {
|
||||
filters: Object,
|
||||
filters: Object as PropType<Filters>,
|
||||
playerHighlighted: String
|
||||
},
|
||||
emits: ['highlightPlayer'],
|
||||
@@ -36,6 +26,12 @@ export default defineComponent({
|
||||
mounted () {
|
||||
this.refreshColors++
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
store: useKillStore(),
|
||||
refreshColors: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
colors () {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
@@ -59,14 +55,26 @@ export default defineComponent({
|
||||
return colors
|
||||
},
|
||||
chart () {
|
||||
const colors = ['cyan', 'green', 'orange', 'pink', 'purple', 'red', 'yellow']
|
||||
const colors = [
|
||||
'cyan',
|
||||
'green',
|
||||
'orange',
|
||||
'pink',
|
||||
'purple',
|
||||
'red',
|
||||
'yellow'
|
||||
]
|
||||
const chartData = {
|
||||
datasets: [
|
||||
{
|
||||
label: 'Weapons',
|
||||
labels: this.sortedWeaponList || [] as string[],
|
||||
borderColor: (context: any) => this.colors.fg,
|
||||
backgroundColor: (context: any) => { return this.colors[colors[(context.dataIndex * 3) % colors.length]] },
|
||||
labels: this.sortedWeaponList || ([] as string[]),
|
||||
borderColor: () => this.colors.fg,
|
||||
backgroundColor: (context: any) => {
|
||||
return this.colors[
|
||||
colors[(context.dataIndex * 3) % colors.length]
|
||||
]
|
||||
},
|
||||
data: [] as number[]
|
||||
}
|
||||
]
|
||||
@@ -74,7 +82,7 @@ export default defineComponent({
|
||||
if (!this.sortedWeaponList) {
|
||||
return chartData
|
||||
}
|
||||
chartData.datasets[0].data = this.sortedWeaponList.map(e => {
|
||||
chartData.datasets[0].data = this.sortedWeaponList.map((e) => {
|
||||
if (!this.weapons) {
|
||||
return 0
|
||||
}
|
||||
@@ -103,10 +111,9 @@ export default defineComponent({
|
||||
borderWidth: 2,
|
||||
color: this.colors.bg,
|
||||
display: (context: any) => {
|
||||
return context.dataIndex === context.dataset.labels.length - 1 ? true : 'auto'
|
||||
},
|
||||
font: {
|
||||
weight: 'bold'
|
||||
return context.dataIndex === context.dataset.labels.length - 1
|
||||
? true
|
||||
: 'auto'
|
||||
},
|
||||
padding: 6
|
||||
},
|
||||
@@ -124,13 +131,18 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return options
|
||||
},
|
||||
weapons (): { [key: string]: Weapon } {
|
||||
return this.store.getters.getWeaponList(this.filters)
|
||||
const { weapon: _, ...withoutWeapon } = this.filters || {}
|
||||
const data = this.store.getWeaponList(withoutWeapon)?.data
|
||||
if (!data) {
|
||||
this.store.fetchWeapons(withoutWeapon)
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
},
|
||||
sortedWeaponList (): string[] {
|
||||
if (!this.weapons) return []
|
||||
@@ -146,13 +158,6 @@ export default defineComponent({
|
||||
})
|
||||
return weapons
|
||||
}
|
||||
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
store: useStore(),
|
||||
refreshColors: 0
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -165,7 +170,7 @@ export default defineComponent({
|
||||
|
||||
@media only screen and (max-width: 922px) {
|
||||
canvas {
|
||||
display: none !important
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user