Player marker improvements

- Use <meter> for health/armor
- Condense small/body image properties into single image size option
- Remove image-size dependant margins
- Add circle alongside label for indicating player position
This commit is contained in:
James Lyne 2021-09-29 01:30:35 +01:00
parent 146a5efc82
commit 770c9242a8
9 changed files with 124 additions and 77 deletions

View File

@ -48,9 +48,8 @@ export default defineComponent({
//The player marker
marker = new PlayerMarker(props.player, {
smallFace: componentSettings.value!.smallFaces,
showSkinFace: componentSettings.value!.showSkinFaces,
showBody: componentSettings.value!.showBodies,
showSkin: componentSettings.value!.showSkins,
imageSize: componentSettings.value!.imageSize,
showHealth: componentSettings.value!.showHealth,
showArmor: componentSettings.value!.showArmor,
pane: 'players',

View File

@ -67,7 +67,7 @@ export default defineComponent({
updatePlayerImage = async () => {
image.value = defaultImage;
if(store.state.components.playerMarkers && store.state.components.playerMarkers.showSkinFaces) {
if(store.state.components.playerMarkers && store.state.components.playerMarkers.showSkins) {
try {
const result = await getMinecraftHead(props.target, '16');
image.value = result.src;

View File

@ -91,7 +91,7 @@ export default defineComponent({
let image = ref(defaultImage);
onMounted(() => {
if(store.state.components.playerMarkers && store.state.components.playerMarkers.showSkinFaces) {
if(store.state.components.playerMarkers && store.state.components.playerMarkers.showSkins) {
getMinecraftHead(props.player, '16').then((result) => image.value = result.src).catch(() => {});
}
});

7
src/index.d.ts vendored
View File

@ -320,13 +320,14 @@ interface LiveAtlasPlayerMarkerConfig {
hideByDefault: boolean;
layerName: string;
layerPriority: number;
showBodies: boolean;
showSkinFaces: boolean;
imageSize: LiveAtlasPlayerImageSize;
showSkins: boolean;
showHealth: boolean;
showArmor: boolean;
smallFaces: boolean;
}
export type LiveAtlasPlayerImageSize = 'none' | 'small' | 'large' | 'body';
interface LiveAtlasChatBoxConfig {
allowUrlName: boolean;
showPlayerFaces: boolean;

View File

@ -20,7 +20,7 @@
import {BaseIconOptions, DomUtil, Icon, Layer, LayerOptions, Util} from 'leaflet';
import {getMinecraftHead} from '@/util';
import playerImage from '@/assets/images/player_face.png';
import {LiveAtlasPlayer} from "@/index";
import {LiveAtlasPlayer, LiveAtlasPlayerImageSize} from "@/index";
const noSkinImage: HTMLImageElement = document.createElement('img');
noSkinImage.height = 16;
@ -42,9 +42,8 @@ noSkinImage.src = smallImage.src = largeImage.src = bodyImage.src = playerImage;
noSkinImage.className = smallImage.className = largeImage.className = bodyImage.className = 'player__icon';
export interface PlayerIconOptions extends BaseIconOptions {
smallFace: boolean,
showSkinFace: boolean,
showBody: boolean,
imageSize: LiveAtlasPlayerImageSize,
showSkin: boolean,
showHealth: boolean,
showArmor: boolean,
}
@ -60,10 +59,8 @@ export class PlayerIcon extends Layer implements Icon<PlayerIconOptions> {
private _currentName?: string;
private _playerHealth?: HTMLDivElement;
private _playerHealthBar?: HTMLDivElement;
private _playerArmor?: HTMLDivElement;
private _playerArmorBar?: HTMLDivElement;
private _playerHealth?: HTMLMeterElement;
private _playerArmor?: HTMLMeterElement;
constructor(player: LiveAtlasPlayer, options: PlayerIconOptions) {
super(options as LayerOptions);
@ -77,7 +74,6 @@ export class PlayerIcon extends Layer implements Icon<PlayerIconOptions> {
}
const player = this._player;
let offset = 8;
this._container = document.createElement('div');
@ -90,21 +86,23 @@ export class PlayerIcon extends Layer implements Icon<PlayerIconOptions> {
this._playerName.className = 'player__name';
this._playerName.innerHTML = this._currentName = player.displayName;
if (this.options.showSkinFace) {
if (this.options.showSkin) {
let size;
if (this.options.smallFace) {
switch(this.options.imageSize) {
case 'small':
this._playerImage = smallImage.cloneNode() as HTMLImageElement;
size = '16';
offset = 8;
} else if(this.options.showBody) {
break;
case 'body':
this._playerImage = bodyImage.cloneNode() as HTMLImageElement;
size = 'body';
offset = 16;
} else {
break;
default:
this._playerImage = largeImage.cloneNode() as HTMLImageElement;
size = '32';
offset = 16;
}
getMinecraftHead(player, size).then(head => {
@ -114,37 +112,28 @@ export class PlayerIcon extends Layer implements Icon<PlayerIconOptions> {
this._playerImage = noSkinImage.cloneNode(false) as HTMLImageElement;
}
this._container.appendChild(this._playerImage);
this._container.appendChild(this._playerInfo);
this._playerInfo.appendChild(this._playerImage);
this._playerInfo.appendChild(this._playerName);
this._container.appendChild(this._playerInfo);
if (this.options.showHealth) {
this._playerHealth = document.createElement('div');
this._playerHealth = document.createElement('meter');
this._playerHealth.className = 'player__health';
this._playerHealth.hidden = true;
this._playerHealth.max = 100;
this._playerHealthBar = document.createElement('div');
this._playerHealthBar.className = 'player__health-bar';
this._playerHealth.appendChild(this._playerHealthBar);
this._playerInfo.appendChild(this._playerHealth);
}
if (this.options.showArmor) {
this._playerArmor = document.createElement('div');
this._playerArmor = document.createElement('meter');
this._playerArmor.className = 'player__armor';
this._playerArmor.hidden = true;
this._playerArmor.max = 100;
this._playerArmorBar = document.createElement('div');
this._playerArmorBar.className = 'player__armor-bar';
this._playerArmor.appendChild(this._playerArmorBar);
this._playerInfo.appendChild(this._playerArmor);
}
this._container.style.marginTop = `-${offset}px`;
this._container.style.marginLeft = `-${offset}px`;
return this._container;
}
@ -165,7 +154,7 @@ export class PlayerIcon extends Layer implements Icon<PlayerIconOptions> {
if(this.options.showHealth) {
if (this._player.health !== undefined) {
this._playerHealth!.hidden = false;
this._playerHealthBar!.style.width = Math.ceil(this._player.health * 2.5) + 'px';
this._playerHealth!.value = this._player.health * 5;
} else {
this._playerHealth!.hidden = true;
}
@ -174,7 +163,7 @@ export class PlayerIcon extends Layer implements Icon<PlayerIconOptions> {
if(this.options.showArmor) {
if(this._player.armor !== undefined) {
this._playerArmor!.hidden = false;
this._playerArmorBar!.style.width = Math.ceil(this._player.armor * 2.5) + 'px';
this._playerArmor!.value = this._player.armor * 5;
} else {
this._playerArmor!.hidden = true;
}

View File

@ -16,14 +16,13 @@
import {LatLng, MarkerOptions, Marker, Map, Util} from 'leaflet';
import {PlayerIcon} from "@/leaflet/icon/PlayerIcon";
import {LiveAtlasPlayer} from "@/index";
import {LiveAtlasPlayer, LiveAtlasPlayerImageSize} from "@/index";
import {watch} from "@vue/runtime-core";
import {WatchStopHandle} from "vue";
export interface PlayerMarkerOptions extends MarkerOptions {
smallFace: boolean,
showSkinFace: boolean,
showBody: boolean,
showSkin: boolean,
imageSize: LiveAtlasPlayerImageSize,
showHealth: boolean,
showArmor: boolean,
}
@ -40,9 +39,8 @@ export class PlayerMarker extends Marker {
this._player = player;
this._PlayerIcon = options.icon = new PlayerIcon(player, {
smallFace: options.smallFace,
showSkinFace: options.showSkinFace,
showBody: options.showBody,
imageSize: options.imageSize,
showSkin: options.showSkin,
showHealth: options.showHealth,
showArmor: options.showArmor,
});

View File

@ -15,10 +15,18 @@
*/
import {
HeadQueueEntry, LiveAtlasArea, LiveAtlasCircle, LiveAtlasComponentConfig,
LiveAtlasDimension, LiveAtlasLine, LiveAtlasMarker,
LiveAtlasMarkerSet, LiveAtlasPartialComponentConfig,
LiveAtlasPlayer, LiveAtlasServerConfig, LiveAtlasServerDefinition,
HeadQueueEntry,
LiveAtlasArea,
LiveAtlasCircle,
LiveAtlasComponentConfig,
LiveAtlasDimension,
LiveAtlasLine,
LiveAtlasMarker,
LiveAtlasMarkerSet,
LiveAtlasPartialComponentConfig,
LiveAtlasPlayer,
LiveAtlasServerConfig,
LiveAtlasServerDefinition,
LiveAtlasServerMessageConfig,
LiveAtlasWorldDefinition
} from "@/index";
@ -90,11 +98,10 @@ export default class Pl3xmapMapProvider extends MapProvider {
hideByDefault: !!worldResponse.player_tracker?.default_hidden,
layerName: worldResponse.player_tracker?.label || '',
layerPriority: worldResponse.player_tracker?.priority,
showBodies: false,
showSkinFaces: true,
imageSize: 'small',
showSkins: true,
showHealth: !!worldResponse.player_tracker?.nameplates?.show_health,
showArmor: !!worldResponse.player_tracker?.nameplates?.show_armor,
smallFaces: true,
}
} else {
worldConfig.components.playerMarkers = undefined;

View File

@ -24,28 +24,68 @@
&.marker--player {
transition: transform 0.3s ease-in 0s;
height: 1.2rem;
margin-left: -0.6rem;
margin-top: -0.6rem;
&:before {
content: '';
display: block;
width: 0.8rem;
height: 0.8rem;
position: absolute;
left: 0;
background-color: var(--text-base);
border: 0.2rem solid var(--background-dark);
border-radius: 50%;
}
.marker__label {
display: block;
display: grid !important;
grid-template-columns: min-content 1fr;
grid-template-rows: 1fr min-content min-content min-content 1fr;
grid-auto-flow: column;
column-gap: 0.5rem;
margin-left: 1.6rem;
background-clip: padding-box;
padding-right: 0.5rem;
align-items: center;
}
.player__icon {
grid-row: 1 / -1;
}
.player__name {
grid-row: 2;
}
.player__health,
.player__armor {
width: 50px;
}
width: 5rem;
height: 0.7rem;
display: block;
.player__health,
.player__armor,
.player__health-bar,
.player__armor-bar {
height: 7px;
&::-webkit-meter-inner-element,
&::-webkit-meter-bar {
border-radius: 0;
background: none;
border: none;
box-shadow: none;
image-rendering: crisp-edges;
image-rendering: pixelated;
}
}
.player__health {
background: url(../assets/images/heart_depleted.png) repeat-x left center;
}
.player__health-bar {
.player__health::-webkit-meter-optimum-value {
background: url(../assets/images/heart.png) repeat-x left center;
}
.player__health::-moz-meter-bar {
background: url(../assets/images/heart.png) repeat-x left center;
}
@ -53,7 +93,11 @@
background: url(../assets/images/armor_depleted.png) repeat-x left center;
}
.player__armor-bar {
.player__armor::-webkit-meter-optimum-value {
background: url(../assets/images/armor.png) repeat-x left center;
}
.player__armor::-moz-meter-bar {
background: url(../assets/images/armor.png) repeat-x left center;
}
}

View File

@ -16,11 +16,15 @@
import {DynmapMarkerSetUpdates, DynmapTileUpdate, DynmapUpdate} from "@/dynmap";
import {
LiveAtlasArea, LiveAtlasChat,
LiveAtlasArea,
LiveAtlasChat,
LiveAtlasCircle,
LiveAtlasComponentConfig, LiveAtlasDimension,
LiveAtlasComponentConfig,
LiveAtlasDimension,
LiveAtlasLine,
LiveAtlasMarker, LiveAtlasServerConfig, LiveAtlasServerMessageConfig,
LiveAtlasMarker,
LiveAtlasServerConfig,
LiveAtlasServerMessageConfig,
LiveAtlasWorldDefinition
} from "@/index";
import {getPoints} from "@/util/areas";
@ -150,11 +154,16 @@ export function buildComponents(response: any): LiveAtlasComponentConfig {
hideByDefault: component.hidebydefault || false,
layerName: component.label || "Players",
layerPriority: component.layerprio || 0,
showBodies: component.showplayerbody || false,
showSkinFaces: component.showplayerfaces || false,
showSkins: component.showplayerfaces || false,
imageSize: 'large',
showHealth: component.showplayerhealth || false,
showArmor: component.showplayerhealth || false,
smallFaces: component.smallplayerfaces || false,
}
if(component.smallplayerfaces) {
components.playerMarkers.imageSize = 'small'
} else if(component.showplayerbody) {
components.playerMarkers.imageSize = 'body';
}
break;