mirror of
https://github.com/Piwigo/Piwigo.git
synced 2026-10-30 21:52:00 +01:00
issue #2560 people avatars on the photo page, highlight the face on the photo
This commit is contained in:
1 parent
91f25e98ab
commit
8e818c9af4
7 files changed
+261
-1
No files matched your search
@@ -808,6 +808,67 @@ SELECT
|
||||
return query2array($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* People of an image shown in the gallery.
|
||||
*/
|
||||
function get_image_visible_people(int $image_id)
|
||||
{
|
||||
$query = '
|
||||
SELECT
|
||||
p.id,
|
||||
p.name,
|
||||
p.url_name,
|
||||
ip.box_x1,
|
||||
ip.box_y1,
|
||||
ip.box_x2,
|
||||
ip.box_y2,
|
||||
ip.image_width,
|
||||
ip.image_height
|
||||
FROM '.IMAGE_PERSON_TABLE.' AS ip
|
||||
INNER JOIN '.PERSONS_TABLE.' AS p ON p.id = ip.person_id
|
||||
WHERE ip.image_id = '.$image_id.'
|
||||
AND p.is_visible = \'true\'
|
||||
ORDER BY p.name
|
||||
;';
|
||||
|
||||
return query2array($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Background size and position (%) showing the face of an image_person in a square.
|
||||
*/
|
||||
function get_person_face_background(array $image_person)
|
||||
{
|
||||
if (!isset($image_person['box_x1']) or empty($image_person['image_width']) or empty($image_person['image_height']))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$side = min(
|
||||
$image_person['box_x2'] - $image_person['box_x1'],
|
||||
$image_person['box_y2'] - $image_person['box_y1']
|
||||
);
|
||||
if ($side <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$position = function($center, $image_size) use ($side)
|
||||
{
|
||||
if ($image_size <= $side)
|
||||
{
|
||||
return 50;
|
||||
}
|
||||
return max(0, min(100, ($center - $side / 2) / ($image_size - $side) * 100));
|
||||
};
|
||||
|
||||
return array(
|
||||
'size' => sprintf('%.2F', $image_person['image_width'] / $side * 100),
|
||||
'x' => sprintf('%.2F', $position(($image_person['box_x1'] + $image_person['box_x2']) / 2, $image_person['image_width'])),
|
||||
'y' => sprintf('%.2F', $position(($image_person['box_y1'] + $image_person['box_y2']) / 2, $image_person['image_height'])),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the images of a person, with the association fields, the thumbnail
|
||||
* url and the medium url (to crop the face). Admin view: photo permissions
|
||||
|
||||
@@ -534,3 +534,5 @@ $lang['If you changed your password because you think it was stolen, we recommen
|
||||
$lang['Too many attempts, please try later..'] = 'Too many attempts, please try later..';
|
||||
$lang['Verification successful! You can now choose a new password.'] = 'Verification successful! You can now choose a new password.';
|
||||
$lang['If your account exists, a verification code has been sent to your email address.'] = 'If your account exists, a verification code has been sent to your email address.';
|
||||
$lang['People'] = 'People';
|
||||
$lang['Click to highlight on the photo'] = 'Click to highlight on the photo';
|
||||
@@ -533,3 +533,5 @@ $lang['If you changed your password because you think it was stolen, we recommen
|
||||
$lang['Too many attempts, please try later..'] = 'Trop de tentatives, veuillez réessayer plus tard..';
|
||||
$lang['Verification successful! You can now choose a new password.'] = 'Vérification réussie ! Vous pouvez maintenant choisir un nouveau mot de passe.';
|
||||
$lang['If your account exists, a verification code has been sent to your email address.'] = 'Si votre compte existe, un code de vérification a été envoyé à votre adresse email.';
|
||||
$lang['People'] = 'Personnes';
|
||||
$lang['Click to highlight on the photo'] = 'Cliquer pour repérer sur la photo';
|
||||
+17
-1
@@ -892,7 +892,8 @@ $infos['INFO_VISITS'] = $picture['current']['hit'];
|
||||
$infos['INFO_FILE'] = $picture['current']['file'];
|
||||
|
||||
$template->assign($infos);
|
||||
$template->assign('display_info', unserialize($conf['picture_informations']));
|
||||
$display_info = unserialize($conf['picture_informations']);
|
||||
$template->assign('display_info', $display_info);
|
||||
|
||||
// related tags
|
||||
$tags = get_common_tags( array($page['image_id']), -1);
|
||||
@@ -921,6 +922,21 @@ if ( count($tags) )
|
||||
}
|
||||
}
|
||||
|
||||
// related people
|
||||
if (!empty($display_info['people']) and can_see_people())
|
||||
{
|
||||
$people = get_image_visible_people($page['image_id']);
|
||||
if (count($people))
|
||||
{
|
||||
foreach ($people as $i => $person)
|
||||
{
|
||||
$people[$i]['face'] = get_person_face_background($person);
|
||||
$people[$i]['initial'] = mb_strtoupper(mb_substr($person['name'], 0, 1));
|
||||
}
|
||||
$template->assign('related_people', $people);
|
||||
}
|
||||
}
|
||||
|
||||
// related categories
|
||||
if ( count($related_categories)==1 and
|
||||
isset($page['category']) and
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
.people-avatars {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
.people-avatar {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: 3px solid #ffffff;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 0 1px #999999;
|
||||
background-color: #cccccc;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.people-avatar:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.people-avatar:hover,
|
||||
.people-avatar:focus,
|
||||
.people-avatar-selected {
|
||||
border-color: #FFA646;
|
||||
}
|
||||
|
||||
.people-avatar-empty {
|
||||
cursor: default;
|
||||
color: #555555;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
line-height: 36px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.people-avatar-empty:hover {
|
||||
border-color: #ffffff;
|
||||
}
|
||||
|
||||
.people-tooltip {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
top: calc(100% + 8px);
|
||||
left: 0;
|
||||
padding: 6px 10px;
|
||||
background: #e0e0e0;
|
||||
color: #222222;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
|
||||
line-height: 18px;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.people-tooltip b,
|
||||
.people-tooltip i {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.people-avatar:hover .people-tooltip,
|
||||
.people-avatar:focus .people-tooltip {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.people-frame {
|
||||
position: absolute;
|
||||
z-index: 50;
|
||||
border: 3px solid #FFA646;
|
||||
box-sizing: border-box;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.people-frame-name {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: -3px;
|
||||
padding: 0 6px;
|
||||
background: #FFA646;
|
||||
color: #493C21;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
jQuery(function($) {
|
||||
const $avatars = $('.people-avatar[data-box]');
|
||||
if (!$avatars.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $frame = $('<div class="people-frame"><span class="people-frame-name"></span></div>').hide().appendTo('body');
|
||||
let $selected = null;
|
||||
|
||||
function show_frame($avatar) {
|
||||
const img = document.getElementById('theMainImage');
|
||||
if (!img || img.hasAttribute('data-src')) {
|
||||
$frame.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = img.getBoundingClientRect();
|
||||
if (!rect.width) {
|
||||
$frame.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const box = String($avatar.data('box')).split(',').map(Number);
|
||||
const scale = rect.width / Number($avatar.data('width'));
|
||||
|
||||
$frame.css({
|
||||
left: rect.left + window.scrollX + box[0] * scale,
|
||||
top: rect.top + window.scrollY + box[1] * scale,
|
||||
width: (box[2] - box[0]) * scale,
|
||||
height: (box[3] - box[1]) * scale,
|
||||
});
|
||||
$frame.find('.people-frame-name').text($avatar.data('name'));
|
||||
$frame.show();
|
||||
}
|
||||
|
||||
function refresh_frame() {
|
||||
if ($selected) {
|
||||
show_frame($selected);
|
||||
}
|
||||
else {
|
||||
$frame.hide();
|
||||
}
|
||||
}
|
||||
|
||||
$avatars
|
||||
.on('mouseenter focus', function() {
|
||||
show_frame($(this));
|
||||
})
|
||||
.on('mouseleave blur', refresh_frame)
|
||||
.on('click keydown', function(e) {
|
||||
if ('keydown' == e.type && 'Enter' != e.key && ' ' != e.key) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
const $avatar = $(this);
|
||||
$avatars.removeClass('people-avatar-selected');
|
||||
|
||||
if ($selected && $selected.is($avatar)) {
|
||||
$selected = null;
|
||||
}
|
||||
else {
|
||||
$selected = $avatar;
|
||||
$avatar.addClass('people-avatar-selected');
|
||||
}
|
||||
|
||||
show_frame($avatar);
|
||||
});
|
||||
|
||||
$(window).on('resize', refresh_frame);
|
||||
$('#theMainImage').on('load', refresh_frame);
|
||||
});
|
||||
@@ -215,6 +215,24 @@ y.callService(
|
||||
</dd>
|
||||
</div>
|
||||
{/if}
|
||||
{if isset($related_people)}
|
||||
{combine_css path="themes/default/css/people.css"}
|
||||
{combine_script id='picture_people' load='footer' require='jquery' path='themes/default/js/picture_people.js'}
|
||||
<div id="People" class="imageInfo">
|
||||
<dt>{'People'|@translate}</dt>
|
||||
<dd><div class="people-avatars">
|
||||
{foreach from=$related_people item=person}
|
||||
{if isset($person.face)}
|
||||
<span class="people-avatar" tabindex="0" data-name="{$person.name|escape:html}" data-box="{$person.box_x1},{$person.box_y1},{$person.box_x2},{$person.box_y2}" data-width="{$person.image_width}" style="background-image:url('{$current.selected_derivative->get_url()}');background-size:{$person.face.size}% auto;background-position:{$person.face.x}% {$person.face.y}%;">
|
||||
{else}
|
||||
<span class="people-avatar people-avatar-empty">{$person.initial}
|
||||
{/if}
|
||||
<span class="people-tooltip"><b>{$person.name}</b>{if isset($person.face)}<i>{'Click to highlight on the photo'|@translate}</i>{/if}</span>
|
||||
</span>
|
||||
{/foreach}
|
||||
</div></dd>
|
||||
</div>
|
||||
{/if}
|
||||
{if $display_info.categories and isset($related_categories)}
|
||||
<div id="Categories" class="imageInfo">
|
||||
<dt>{'Albums'|@translate}</dt>
|
||||
|
||||
Reference in new issue
Block a user