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: this.options.icon,
label: this.options.label,
dimensions: this.options.dimensions,
iconSize: this.options.dimensions,
showLabel: true,
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;
label: string;
dimensions: PointTuple;
showLabel: boolean;
isHtml?: boolean;
className?: string;
}
export class DynmapIcon extends L.Icon<DynmapIconOptions> {
export class DynmapIcon extends L.DivIcon {
static defaultOptions: DynmapIconOptions = {
icon: 'default',
label: '',
dimensions: [16,16],
iconSize: [16, 16],
showLabel: false,
isHtml: false,
className: '',
};
private _container?: HTMLDivElement;
// @ts-ignore
options: DynmapIconOptions;
constructor(options: DynmapIconOptions) {
super(Object.assign(DynmapIcon.defaultOptions, options));
@ -29,27 +29,19 @@ export class DynmapIcon extends L.Icon<DynmapIconOptions> {
L.DomUtil.remove(oldIcon);
}
const
const div = document.createElement('div'),
img = document.createElement('img'),
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');
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';
const sizeClass = [size.x, size.y].join('x');
if(this.options.className) {
this._container.classList.add(this.options.className);
}
img.classList.add('markerIcon', `markerIcon${this.options.dimensions.join('x')}`);
img.className = `marker__icon marker__icon--${sizeClass}`;
img.src = url;
label.classList.add(this.options.showLabel ? 'markerName-show' : 'markerName');
label.classList.add(/*'markerName_' + set.id,*/ `markerName${this.options.dimensions.join('x')}`);
label.className = this.options.showLabel ? 'marker__label marker__label--show' : 'marker__label';
label.classList.add(/*'markerName_' + set.id,*/ `marker__label--${sizeClass}`);
if (this.options.isHtml) {
label.insertAdjacentHTML('afterbegin', this.options.label);
@ -57,13 +49,20 @@ export class DynmapIcon extends L.Icon<DynmapIconOptions> {
label.textContent = this.options.label;
}
// this._container.insertAdjacentElement('beforeend', img);
// this._container.insertAdjacentElement('beforeend', label);
// @ts-ignore
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 {DynmapPlayer} from "@/dynmap";
import Util from '@/util';
const noSkinImage: HTMLImageElement = document.createElement('img');
noSkinImage.height = 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 {
smallFace: boolean,
@ -17,13 +32,13 @@ export class PlayerIcon extends L.DivIcon {
private readonly _player: DynmapPlayer;
private _container?: HTMLDivElement;
private _playerImage?: HTMLImageElement;
private _playerInfo?: HTMLSpanElement;
private _playerName?: HTMLSpanElement;
private _playerHealth?: HTMLDivElement;
private _playerHealthBg?: HTMLDivElement;
private _playerHealthBar?: HTMLDivElement;
private _playerArmourBg?: HTMLDivElement;
private _playerArmourBar?: HTMLDivElement;
private _playerArmor?: HTMLDivElement;
private _playerArmorBar?: HTMLDivElement;
// @ts-ignore
options: PlayerIconOptions;
@ -42,62 +57,57 @@ export class PlayerIcon extends L.DivIcon {
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.classList.add(this.options.smallFace ? 'playerNameSm' : 'playerName');
this._playerName.className = 'player__name';
this._playerName.innerText = player.name;
if (this.options.showSkinFace) {
this._playerImage = document.createElement('img');
this._playerImage.classList.add(this.options.smallFace ? 'playerIconSm' : 'playerIcon');
let size;
// if (this.options.smallFace) {
// getMinecraftHead(player.account, 16, head => {
// this._playerImage!.src = head.src;
// });
// } else if (this.options.showBody) {
// getMinecraftHead(player.account, 'body', head => {
// this._playerImage!.src = head.src;
// });
// } else {
// getMinecraftHead(player.account, 32, head => {
// this._playerImage!.src = head.src;
// });
// }
if (this.options.smallFace) {
this._playerImage = smallImage.cloneNode() as HTMLImageElement;
size = '16';
} else if(this.options.showBody) {
this._playerImage = bodyImage.cloneNode() as HTMLImageElement;
size = 'body';
} else {
this._playerImage = largeImage.cloneNode() as HTMLImageElement;
size = '32';
}
Util.getMinecraftHead(player, size).then(head => {
this._playerImage!.src = head.src;
});
} else {
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._playerName);
this._container.appendChild(this._playerInfo);
this._playerInfo.appendChild(this._playerName);
if (this.options.showHealth) {
this._playerHealth = document.createElement('div');
this._playerHealth.className = 'player__health';
this._playerHealth.classList.add(this.options.smallFace ? 'healthContainerSm' : 'healthContainer');
this._container.appendChild(this._playerHealth)
this._playerArmor = document.createElement('div');
this._playerArmor.className = 'player__armor';
this._playerInfo.appendChild(this._playerHealth);
this._playerInfo.appendChild(this._playerArmor);
this._playerHealthBar = document.createElement('div');
this._playerHealthBar.classList.add('playerHealth');
this._playerHealthBar.className = 'player__health-bar';
this._playerArmourBar = document.createElement('div');
this._playerArmourBar.classList.add('playerHealth');
this._playerArmorBar = document.createElement('div');
this._playerArmorBar.className = 'player__armor-bar';
this._playerHealthBg = document.createElement('div');
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;
this._playerHealth.hidden = this._playerArmor.hidden = true;
} else {
this._playerName.classList.add('playerNameNoHealth');
}
@ -115,10 +125,12 @@ export class PlayerIcon extends L.DivIcon {
if(this.options.showHealth) {
if (this._player.health !== undefined && this._player.armor !== undefined) {
this._playerHealth!.hidden = false;
this._playerArmor!.hidden = false;
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 {
this._playerHealth!.hidden = true;
this._playerArmor!.hidden = true;
}
}
}

View File

@ -281,29 +281,49 @@ button {
* players on the map
*/
/* smooth player movements (contrib from KillahKiwi) */
.dynmap .playerMarker {
.marker {
display: flex;
align-items: center;
&.marker--player {
transition: transform 0.3s ease-in 0s;
.marker__label {
display: block;
}
.dynmap .playerIcon {
margin-top: -16px;
margin-left: -16px;
width: 32px;
height: 32px;
.player__health,
.player__armor {
width: 50px;
}
.dynmap .playerIconSm {
margin-top: -8px;
margin-left: -8px;
width: 16px;
height: 16px;
.player__health,
.player__armor,
.player__health-bg,
.player__armor-bg {
height: 7px;
}
.dynmap .playerName {
position: absolute;
top: -19px;
left: 18px;
.player__health {
background: url(../assets/images/heart.png) repeat-x left center;
}
.player__health-bg {
background: url(../assets/images/heart_depleted.png) repeat-x left center;
}
.player__armor {
background: url(../assets/images/armor.png) repeat-x left center;
}
.player__armor-bg {
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;
@ -311,81 +331,21 @@ button {
color: $global-text-color;
background: #121212;
padding: 0.2rem;
}
display: none;
max-width: 25vw;
text-overflow: ellipsis;
overflow: hidden;
.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 {
&.marker__label--show {
display: block;
position: absolute;
top: 1px;
left: 18px;
width: 50px;
background: rgba(0,0,0,0.6);
padding: 2px;
border-radius: 3px;
z-index: 21;
}
}
.dynmap .healthContainerSm {
&:hover .marker__label {
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;
background: url(../assets/images/heart.png) repeat-x left center;
}
.dynmap .playerHealthBackground {
height: 7px;
width: 50px;
background: url(../assets/images/heart_depleted.png) repeat-x left center;
}
.dynmap .playerArmor {
height: 7px;
background: url(../assets/images/armor.png) repeat-x left center;
}
.dynmap .playerArmorBackground {
height: 7px;
width: 50px;
background: url(../assets/images/armor_depleted.png) repeat-x left center;
}
/*******************
* Compass
*/
@ -515,7 +475,6 @@ button {
overflow-y: auto !important;
}
.messagerow {
position: relative;
max-height: 200px;
@ -539,68 +498,10 @@ button {
left: 0;
}
.leaflet-popup {
color: black;
}
.balloonmessage {
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 {
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;
}
}