Marker improvements

- Refactor html and classnames
- Fix/refactor styles
- Re-implement player head images
This commit is contained in:
James Lyne 2020-12-10 02:21:42 +00:00
parent 5d0f26917d
commit 404121188b
5 changed files with 197 additions and 232 deletions

View File

@ -46,7 +46,7 @@ export default defineComponent({
icon: new DynmapIcon({ icon: new DynmapIcon({
icon: this.options.icon, icon: this.options.icon,
label: this.options.label, label: this.options.label,
dimensions: this.options.dimensions, iconSize: this.options.dimensions,
showLabel: true, showLabel: true,
isHtml: this.options.isHTML, isHtml: this.options.isHTML,
}), }),

View File

@ -1,24 +1,24 @@
import L, {PointTuple} from 'leaflet'; import L, {DivIconOptions, PointExpression, PointTuple} from 'leaflet';
export interface DynmapIconOptions { export interface DynmapIconOptions extends DivIconOptions {
icon: string; icon: string;
label: string; label: string;
dimensions: PointTuple;
showLabel: boolean; showLabel: boolean;
isHtml?: boolean; isHtml?: boolean;
className?: string;
} }
export class DynmapIcon extends L.Icon<DynmapIconOptions> { export class DynmapIcon extends L.DivIcon {
static defaultOptions: DynmapIconOptions = { static defaultOptions: DynmapIconOptions = {
icon: 'default', icon: 'default',
label: '', label: '',
dimensions: [16,16], iconSize: [16, 16],
showLabel: false, showLabel: false,
isHtml: false, isHtml: false,
className: '', className: '',
}; };
private _container?: HTMLDivElement;
// @ts-ignore
options: DynmapIconOptions;
constructor(options: DynmapIconOptions) { constructor(options: DynmapIconOptions) {
super(Object.assign(DynmapIcon.defaultOptions, options)); super(Object.assign(DynmapIcon.defaultOptions, options));
@ -29,27 +29,19 @@ export class DynmapIcon extends L.Icon<DynmapIconOptions> {
L.DomUtil.remove(oldIcon); L.DomUtil.remove(oldIcon);
} }
const const div = document.createElement('div'),
img = document.createElement('img'), img = document.createElement('img'),
label = document.createElement('span'), label = document.createElement('span'),
url = `${window.config.url.markers}_markers_/${this.options.icon}.png`; url = `${window.config.url.markers}_markers_/${this.options.icon}.png`,
size = L.point(this.options.iconSize as PointExpression);
// this._container.classList.add('Marker', 'mapMarker'); const sizeClass = [size.x, size.y].join('x');
this._container = document.createElement('div');
this._container.classList.add('leaflet-div-icon');
this._container.style.backgroundColor = 'pink';
this._container.style.width = '16px';
this._container.style.height = '16px';
if(this.options.className) { img.className = `marker__icon marker__icon--${sizeClass}`;
this._container.classList.add(this.options.className);
}
img.classList.add('markerIcon', `markerIcon${this.options.dimensions.join('x')}`);
img.src = url; img.src = url;
label.classList.add(this.options.showLabel ? 'markerName-show' : 'markerName'); label.className = this.options.showLabel ? 'marker__label marker__label--show' : 'marker__label';
label.classList.add(/*'markerName_' + set.id,*/ `markerName${this.options.dimensions.join('x')}`); label.classList.add(/*'markerName_' + set.id,*/ `marker__label--${sizeClass}`);
if (this.options.isHtml) { if (this.options.isHtml) {
label.insertAdjacentHTML('afterbegin', this.options.label); label.insertAdjacentHTML('afterbegin', this.options.label);
@ -57,13 +49,20 @@ export class DynmapIcon extends L.Icon<DynmapIconOptions> {
label.textContent = this.options.label; label.textContent = this.options.label;
} }
// this._container.insertAdjacentElement('beforeend', img); // @ts-ignore
// this._container.insertAdjacentElement('beforeend', label); L.Icon.prototype._setIconStyles.call(this, div, 'icon');
return this._container; div.appendChild(img);
div.appendChild(label);
div.classList.add('marker');
if(this.options.className) {
div.classList.add(this.options.className);
} }
update() { console.log(div.className);
return div;
} }
} }

View File

@ -1,10 +1,25 @@
import L, {MarkerOptions} from 'leaflet'; import L, {MarkerOptions} from 'leaflet';
import {DynmapPlayer} from "@/dynmap"; import {DynmapPlayer} from "@/dynmap";
import Util from '@/util';
const noSkinImage: HTMLImageElement = document.createElement('img'); const noSkinImage: HTMLImageElement = document.createElement('img');
noSkinImage.height = 16; noSkinImage.height = 16;
noSkinImage.width = 16; noSkinImage.width = 16;
noSkinImage.src = 'images/player.png';
const smallImage: HTMLImageElement = document.createElement('img');
smallImage.height = 16;
smallImage.width = 16;
const largeImage: HTMLImageElement = document.createElement('img');
largeImage.height = 32;
largeImage.width = 32;
const bodyImage: HTMLImageElement = document.createElement('img');
bodyImage.height = 32;
bodyImage.width = 32;
noSkinImage.src = smallImage.src = largeImage.src = bodyImage.src = 'images/player.png';
noSkinImage.className = smallImage.className = largeImage.className = bodyImage.className = 'player__icon';
export interface PlayerIconOptions extends MarkerOptions { export interface PlayerIconOptions extends MarkerOptions {
smallFace: boolean, smallFace: boolean,
@ -17,13 +32,13 @@ export class PlayerIcon extends L.DivIcon {
private readonly _player: DynmapPlayer; private readonly _player: DynmapPlayer;
private _container?: HTMLDivElement; private _container?: HTMLDivElement;
private _playerImage?: HTMLImageElement; private _playerImage?: HTMLImageElement;
private _playerInfo?: HTMLSpanElement;
private _playerName?: HTMLSpanElement; private _playerName?: HTMLSpanElement;
private _playerHealth?: HTMLDivElement; private _playerHealth?: HTMLDivElement;
private _playerHealthBg?: HTMLDivElement;
private _playerHealthBar?: HTMLDivElement; private _playerHealthBar?: HTMLDivElement;
private _playerArmourBg?: HTMLDivElement; private _playerArmor?: HTMLDivElement;
private _playerArmourBar?: HTMLDivElement; private _playerArmorBar?: HTMLDivElement;
// @ts-ignore // @ts-ignore
options: PlayerIconOptions; options: PlayerIconOptions;
@ -42,62 +57,57 @@ export class PlayerIcon extends L.DivIcon {
this._container = document.createElement('div'); this._container = document.createElement('div');
this._container.classList.add('Marker', 'playerMarker', 'leaflet-marker-icon'); this._container.classList.add('marker', 'marker--player', 'leaflet-marker-icon');
this._playerInfo = document.createElement('div');
this._playerInfo.className = 'marker__label';
this._playerName = document.createElement('span'); this._playerName = document.createElement('span');
this._playerName.classList.add(this.options.smallFace ? 'playerNameSm' : 'playerName'); this._playerName.className = 'player__name';
this._playerName.innerText = player.name; this._playerName.innerText = player.name;
if (this.options.showSkinFace) { if (this.options.showSkinFace) {
this._playerImage = document.createElement('img'); let size;
this._playerImage.classList.add(this.options.smallFace ? 'playerIconSm' : 'playerIcon');
// if (this.options.smallFace) { if (this.options.smallFace) {
// getMinecraftHead(player.account, 16, head => { this._playerImage = smallImage.cloneNode() as HTMLImageElement;
// this._playerImage!.src = head.src; size = '16';
// }); } else if(this.options.showBody) {
// } else if (this.options.showBody) { this._playerImage = bodyImage.cloneNode() as HTMLImageElement;
// getMinecraftHead(player.account, 'body', head => { size = 'body';
// this._playerImage!.src = head.src; } else {
// }); this._playerImage = largeImage.cloneNode() as HTMLImageElement;
// } else { size = '32';
// getMinecraftHead(player.account, 32, head => { }
// this._playerImage!.src = head.src;
// }); Util.getMinecraftHead(player, size).then(head => {
// } this._playerImage!.src = head.src;
});
} else { } else {
this._playerImage = noSkinImage.cloneNode(false) as HTMLImageElement; this._playerImage = noSkinImage.cloneNode(false) as HTMLImageElement;
this._playerImage.classList.add(this.options.smallFace ? 'playerIconSm' : 'playerIcon');
} }
this._container.appendChild(this._playerImage); this._container.appendChild(this._playerImage);
this._container.appendChild(this._playerName); this._container.appendChild(this._playerInfo);
this._playerInfo.appendChild(this._playerName);
if (this.options.showHealth) { if (this.options.showHealth) {
this._playerHealth = document.createElement('div'); this._playerHealth = document.createElement('div');
this._playerHealth.className = 'player__health';
this._playerHealth.classList.add(this.options.smallFace ? 'healthContainerSm' : 'healthContainer'); this._playerArmor = document.createElement('div');
this._container.appendChild(this._playerHealth) this._playerArmor.className = 'player__armor';
this._playerInfo.appendChild(this._playerHealth);
this._playerInfo.appendChild(this._playerArmor);
this._playerHealthBar = document.createElement('div'); this._playerHealthBar = document.createElement('div');
this._playerHealthBar.classList.add('playerHealth'); this._playerHealthBar.className = 'player__health-bar';
this._playerArmourBar = document.createElement('div'); this._playerArmorBar = document.createElement('div');
this._playerArmourBar.classList.add('playerHealth'); this._playerArmorBar.className = 'player__armor-bar';
this._playerHealthBg = document.createElement('div'); this._playerHealth.hidden = this._playerArmor.hidden = true;
this._playerArmourBg = document.createElement('div');
this._playerHealthBg.classList.add('playerHealthBackground');
this._playerArmourBar.classList.add('playerArmorBackground');
this._playerHealthBg.appendChild(this._playerHealthBar);
this._playerArmourBg.appendChild(this._playerArmourBar);
this._playerHealth.appendChild(this._playerHealthBg);
this._playerHealth.appendChild(this._playerArmourBg);
this._playerHealth.hidden = true;
} else { } else {
this._playerName.classList.add('playerNameNoHealth'); this._playerName.classList.add('playerNameNoHealth');
} }
@ -115,10 +125,12 @@ export class PlayerIcon extends L.DivIcon {
if(this.options.showHealth) { if(this.options.showHealth) {
if (this._player.health !== undefined && this._player.armor !== undefined) { if (this._player.health !== undefined && this._player.armor !== undefined) {
this._playerHealth!.hidden = false; this._playerHealth!.hidden = false;
this._playerArmor!.hidden = false;
this._playerHealthBar!.style.width = Math.ceil(this._player.health * 2.5) + 'px'; this._playerHealthBar!.style.width = Math.ceil(this._player.health * 2.5) + 'px';
this._playerArmourBar!.style.width = Math.ceil(this._player.armor * 2.5) + 'px'; this._playerArmorBar!.style.width = Math.ceil(this._player.armor * 2.5) + 'px';
} else { } else {
this._playerHealth!.hidden = true; this._playerHealth!.hidden = true;
this._playerArmor!.hidden = true;
} }
} }
} }

View File

@ -281,110 +281,70 @@ button {
* players on the map * players on the map
*/ */
/* smooth player movements (contrib from KillahKiwi) */ .marker {
.dynmap .playerMarker { display: flex;
align-items: center;
&.marker--player {
transition: transform 0.3s ease-in 0s; transition: transform 0.3s ease-in 0s;
}
.dynmap .playerIcon { .marker__label {
margin-top: -16px;
margin-left: -16px;
width: 32px;
height: 32px;
}
.dynmap .playerIconSm {
margin-top: -8px;
margin-left: -8px;
width: 16px;
height: 16px;
}
.dynmap .playerName {
position: absolute;
top: -19px;
left: 18px;
z-index:20;
white-space: nowrap;
color: $global-text-color;
background: #121212;
padding: 0.2rem;
}
.dynmap .playerNameSm {
position: absolute;
top: -16px;
left: 10px;
white-space: nowrap;
color: $global-text-color;
background: #121212;
padding: 0.2rem;
}
.dynmap .playerNameNoHealth {
top: -7px;
}
.dynmap .healthContainer {
display: block; display: block;
position: absolute; }
top: 1px;
left: 18px;
.player__health,
.player__armor {
width: 50px; width: 50px;
}
background: rgba(0,0,0,0.6); .player__health,
padding: 2px; .player__armor,
.player__health-bg,
border-radius: 3px; .player__armor-bg {
z-index: 21;
}
.dynmap .healthContainerSm {
display: block;
position: absolute;
top: -2px;
left: 10px;
width: 50px;
background: rgba(0,0,0,0.6);
padding: 2px;
border-radius: 3px;
}
.dynmap .playerHealth {
height: 7px; height: 7px;
}
.player__health {
background: url(../assets/images/heart.png) repeat-x left center; background: url(../assets/images/heart.png) repeat-x left center;
} }
.dynmap .playerHealthBackground {
height: 7px;
width: 50px;
.player__health-bg {
background: url(../assets/images/heart_depleted.png) repeat-x left center; background: url(../assets/images/heart_depleted.png) repeat-x left center;
} }
.dynmap .playerArmor {
height: 7px;
.player__armor {
background: url(../assets/images/armor.png) repeat-x left center; background: url(../assets/images/armor.png) repeat-x left center;
} }
.dynmap .playerArmorBackground {
height: 7px;
width: 50px;
.player__armor-bg {
background: url(../assets/images/armor_depleted.png) repeat-x left center; background: url(../assets/images/armor_depleted.png) repeat-x left center;
} }
}
.marker__label {
flex: 0 0 auto;
margin-left: 2px;
z-index: 20;
white-space: nowrap;
color: $global-text-color;
background: #121212;
padding: 0.2rem;
display: none;
max-width: 25vw;
text-overflow: ellipsis;
overflow: hidden;
&.marker__label--show {
display: block;
}
}
&:hover .marker__label {
display: block;
}
}
/******************* /*******************
* Compass * Compass
@ -515,7 +475,6 @@ button {
overflow-y: auto !important; overflow-y: auto !important;
} }
.messagerow { .messagerow {
position: relative; position: relative;
max-height: 200px; max-height: 200px;
@ -539,68 +498,10 @@ button {
left: 0; left: 0;
} }
.leaflet-popup {
color: black;
}
.balloonmessage { .balloonmessage {
word-wrap: break-word; word-wrap: break-word;
} }
/* Marker styles */
.dynmap .mapMarker .markerName {
display: none;
z-index: 101;
}
.dynmap .mapMarker:hover .markerName,
.dynmap .mapMarker .markerName-show {
display: block;
position: absolute;
z-index: 16;
white-space: nowrap;
color: #fff;
background: rgba(0,0,0,0.6);
padding: 2px;
border-radius: 3px;
}
.dynmap .mapMarker .markerName16x16 {
top: -6px;
left: 10px;
}
.dynmap .mapMarker .markerName8x8 {
top: -4px;
left: 6px;
}
.dynmap .mapMarker .markerName32x32 {
top: -8px;
left: 18px;
}
.dynmap .mapMarker .markerIcon16x16 {
transform: translate(-50%, -50%);
width: 16px;
height: 16px;
}
.dynmap .mapMarker .markerIcon8x8 {
transform: translate(-50%, -50%);
width: 8px;
height: 8px;
}
.dynmap .mapMarker .markerIcon32x32 {
transform: translate(-50%, -50%);
width: 32px;
height: 32px;
}
.dynmap .mapMarker .markerName_offline_players { .dynmap .mapMarker .markerName_offline_players {
font-style: italic; font-style: italic;
} }

53
src/util.ts Normal file
View File

@ -0,0 +1,53 @@
import {DynmapPlayer} from "@/dynmap";
const headCache = new Map<DynmapPlayer, HTMLImageElement>();
export default {
getMinecraftTime(serverTime: number) {
const day = serverTime >= 0 && serverTime < 13700;
return {
serverTime: serverTime,
days: Math.floor((serverTime + 8000) / 24000),
// Assuming it is day at 6:00
hours: (Math.floor(serverTime / 1000) + 6) % 24,
minutes: Math.floor(((serverTime / 1000) % 1) * 60),
seconds: Math.floor(((((serverTime / 1000) % 1) * 60) % 1) * 60),
day: day,
night: !day
};
},
getMinecraftHead(player: DynmapPlayer, size: string): Promise<HTMLImageElement> {
if(headCache.has(player)) {
return Promise.resolve(headCache.get(player) as HTMLImageElement);
}
return new Promise((resolve, reject) => {
const faceImage = new Image();
faceImage.onload = function() {
headCache.set(player, faceImage);
resolve(faceImage);
};
faceImage.onerror = function() {
console.error('Failed to retrieve face of "', player, '" with size "', size, '"!');
reject();
};
const src = (size === 'body') ? `faces/body/${player.name}.png` :`faces/${size}x${size}/${player.name}.png`;
faceImage.src = this.concatURL(window.config.url.markers, src);
});
},
concatURL(base: string, addition: string) {
if(base.indexOf('?') >= 0) {
return base + escape(addition);
}
return base + addition;
}
}