Image annotator is a tweaked structure field, allowing you to add notes to images by pinning them to specific coordinates.
A quick overview :
- Add a pin by clicking anywhere on the image. It acts as a trigger for adding a structure entry, and therefore opens a modal.
- Pins can be dragged once added (Important : if a new pin has just been added, you’ll need to save the page in order to be able to drag the pin).
- Pins are deleted when its associated structure entry is.
- Pins index is updated when structure entries are sorted.
- Pins background color can easily be changed
Basic usage :
imagefieldname:
label: The image that the annotator field will use
type: image
fieldname:
label: Field label
type: imageannotator
src: imagefieldname
fields:
markerid:
type: hidden
x:
type: hidden
y:
type: hidden
customfield:
label: Note
type: text
I initially did it for a client project in order to locate archive photos on street views. I just took a few time to clean it a little, but I haven’t tested every default structure options yet, so any help or feedback from your eventual uses of it are very welcome 
More informations on GitHub :
https://github.com/sylvainjule/kirby-image-annotator
12 Likes
Oops, it was still private. Fixed, thanks ! 
Very cool and useful, gonna test tomorrow.
1 Like
This!!! I needed something like this like a year ago for two sites. How i wish time travel was possible. I ended up using the stand alone version of this but it was a lot of manual work to get it into Kirby.
Good work @sylvainjule.
What a great idea! Very useful indeed. 
@aoimedia added it to https://github.com/jenstornell/kirby-plugins/issues/545 (I was too slow, as always)
I hope you intend to keep it updated to work with Kirby 3 when ever it will be released? This a kind of plugin that, at least I could think of purchase for a license, 10 EUR or so. But I don’t mind the MIT license. 
Really glad you find it useful !
Yes I’m currently diving into Vue for some other projects, hopefully by the time it launches I’ll be able to keep the few fields I have updated a quickly as possible 
Great!
I will try the same thing… with the few fields I have… 
What I’d really
to see as an extension: Using this field in the images view, so the information can be added in file meta data, similar to what @flokosiol did with his crop position plugin…
Thats a great idea. This data should really belong to the image, not a page.
It very much depends on the use case, for mine it was much more interesting to have it in a page as the whole page content revolved around it and I used it to link to subpages.
But you’re definitely right it would be a great option to add ! Added it on my to-do list 
2 Likes
@sylvainjule Well, is it something that can be toggled from a config setting? Some flag that says use file meta or a page field to store the data?
Yep, I agree it depends on the use case, but for myself, can think of more settings were I would like to add the data to the image rather than the page.
Glad to hear you are considering it!
@sylvainjule I haven’t yet tried the plugin, but how does it work out with a lot of pins? My use case is that i build a lot of sites for a particular industry which involves using a geographical map and plotting about 30 locations on it. For my use case, i would like to put a logo or a photo inside the info for a pin, as well as text and a button. Is that possible?
You should see it as a structure field, only the Add button has been replaced by a click somewhere on the image. So you should be able to put anything you could have put within a structure field as field options, photo, text, etc.
No problem to deal with many entries, aside from the messy display you’ll likely end up with on smaller screens. 
I could add a config option to choose the display size of the pin, but for now you could tweak this directly in the CSS.
@texnixe I have put something together on the branch called filesfield
. I keep it there while I do some more testing but you can give it a shot already. To keep it short, if you now use it as a file field, and don’t add any src
option, it should work fine !
@sylvainjule Oh, great, that was quick
, I’ll give it a go as soon as I can.
This plugin is amazing.
I managed to implement it on the file page itself and I can save markers just fine. I would like to create a custom image tag (overwrite the default kirbytext tag) so all images that have saved markers will output the image tag and all saved markers within the .
I kinda managed to create a custom tag, but due to my limited PHP knowledge I’m not really sure how I would retrieve that data for the markers here and print them one after one in the $html.
<?php
Kirby::plugin('your/plugin', [
'tags' => [
'image' => [
'attr' => [
'alt',
'caption',
'class',
'height',
'imgclass',
'link',
'linkclass',
'rel',
'target',
'title',
'width'
],
'html' => function($tag) {
if ($tag->file = $tag->file($tag->value)) {
$tag->src = $tag->file->url();
$tag->alt = $tag->alt ?? $tag->file->alt()->or(' ')->value();
$tag->title = $tag->title ?? $tag->file->title()->value();
$tag->caption = $tag->caption ?? $tag->file->caption()->value();
$tag->ratio = $tag->file->ratio();
$html =
'<figure class="markers">
<img src="' . $tag->src() . '" alt="' . $tag->alt . '">
</figure>';
$html = str_replace(array("\r", "\n"), '', $html);
return $html;
}
}
]
]
]);
Something along those lines should do the trick:
if ($tag->file = $tag->file($tag->value)) {
// (... your current code without returning $html)
if($tag->parent()->markers()->exists()) {
$markers = $tag->parent()->markers()->toStructure();
$markersHtml = '';
foreach($markers as $marker) {
$markersHtml .= '<div class="marker" style="'. markerStyle($marker) .'"></div>';
}
$markersHtml = $markersHtml !== '' ? '<div class="markers">'. $markersHtml .'</div>' : $markersHtml;
$html .= $markersHtml;
}
return $html;
}
@sylvainjule thanks a lot for your help!
I tried your snippet, but getting the following error:
“Call to a member function markers() on null”
<?php
Kirby::plugin('tags/image', [
'tags' => [
'image' => [
'attr' => [
// list of attributes
],
'html' => function($tag) {
if ($tag->file = $tag->file($tag->value)) {
$tag->src = $tag->file->url();
$html =
'<figure class="markers">
<img src="' . $tag->src() . '">
</figure>';
if($tag->parent->markers()->exists()) {
$markers = $tag->parent->markers()->toStructure();
$markersHtml = '';
foreach($markers as $marker) {
$markersHtml .= '<div class="marker" style="'. markerStyle($marker) .'"></div>';
}
$markersHtml = $markersHtml !== '' ? '<div class="markers">'. $markersHtml .'</div>' : $markersHtml;
$html .= $markersHtml;
}
$html = str_replace(array("\r", "\n"), '', $html);
return $html;
}
}
]
]
]);
Apologies, use ->parent()
instead of ->parent
. I have edited the code above.