How to create a custom annotation that is animated?

Q:

Is it possible to create a custom annotation that is animated when viewed in WebViewer? I only need to have it display correctly in WebViewer, it doesn’t need to work in other PDF viewers.

A:

To create HTML elements that you can control the styling of and animate or add JavaScript event handlers to, one approach is to create an HTML annotation and then assign it to a field with a particular name. Then extend the createInnerElement function to apply custom handling for widgets with that name.

In this example it creates a text widget annotation, and note that you will need to use a custom CSS file specified with the css constructor option as shown here https://www.pdftron.com/documentation/web/guides/customizing-styles/

CSS file:

`
.animated-element {
animation: pulse 2s infinite;
}

@keyframes pulse {
0% {
background-color: #001F3F;
}
100% {
background-color: #FF4136;
}
}
`

JavaScript

`

const { docViewer, annotManager, Annotations } = instance;
const fieldManager = annotManager.getFieldManager();

const customFieldName = ‘custom-annotation-field’;

const createInnerElement = Annotations.TextWidgetAnnotation.prototype.createInnerElement;
Annotations.TextWidgetAnnotation.prototype.createInnerElement = function() {
if (this.fieldName !== customFieldName) {
// perform the default behavior for normal text widgets
return createInnerElement.apply(this, arguments);
}

// create custom element and set event handlers on custom element
const element = document.createElement(‘div’);

const animatedClass = ‘animated-element’;

element.addEventListener(‘mouseenter’, () => {
element.classList.add(animatedClass);
});

element.addEventListener(‘mouseleave’, () => {
element.classList.remove(animatedClass);
});

// optional if you need to clean up anything when removed from the DOM
// in this case don’t want to be stuck in the mouse enter state
onRemove(element, () => {
element.classList.remove(animatedClass);
});

return element;
};

function onRemove(element, onDetachCallback) {
const observer = new MutationObserver(function() {
function isDetached(el) {
if (el.parentNode === document) {
return false;
} else if (el.parentNode === null) {
return true;
} else {
return isDetached(el.parentNode);
}
}

if (isDetached(element)) {
onDetachCallback();
}
})

observer.observe(instance.iframeWindow.document, {
childList: true,
subtree: true
});
};

Annotations.WidgetAnnotation.getCustomStyles = widget => {
if (widget.fieldName === customFieldName) {
// optionally set initial CSS styles
return {
backgroundColor: ‘#001F3F
};
}
};

const customField = new Annotations.Forms.Field(customFieldName, {
type: ‘Tx’,
value: ‘’,
});

docViewer.on(‘documentLoaded’, () => {
fieldManager.addField(customField);

createCustomAnnotation({ x: 100, y: 100, width: 100, height: 20, pageNumber: 1});
createCustomAnnotation({ x: 100, y: 200, width: 100, height: 20, pageNumber: 1})
});

const createCustomAnnotation = ({ x, y, width, height, pageNumber }) => {
// create a new custom annotation with the specified attributes
const annot = new Annotations.TextWidgetAnnotation(customField);
annot.X = x;
annot.Y = y;
annot.Width = width;
annot.Height = height;
annot.PageNumber = pageNumber;

annotManager.addAnnotation(annot);
annotManager.drawAnnotations({
pageNumber,
majorRedraw: true
});
};

`