mirror of
https://github.com/jspsych/jsPsych.git
synced 2025-05-10 19:20:55 +00:00
colorized categories!
This commit is contained in:
parent
0825be0443
commit
919f7e63db
@ -22,6 +22,20 @@ const info = <const>{
|
|||||||
default: [],
|
default: [],
|
||||||
array: true,
|
array: true,
|
||||||
},
|
},
|
||||||
|
colors: {
|
||||||
|
type: ParameterType.STRING,
|
||||||
|
array: true,
|
||||||
|
default: [
|
||||||
|
"#e41a1c",
|
||||||
|
"#377eb8",
|
||||||
|
"#4daf4a",
|
||||||
|
"#984ea3",
|
||||||
|
"#ff7f00",
|
||||||
|
"#ffff33",
|
||||||
|
"#a65628",
|
||||||
|
"#f781bf",
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,15 +54,26 @@ class ImageTextAnnotationPlugin implements JsPsychPlugin<Info> {
|
|||||||
private img_container: HTMLElement;
|
private img_container: HTMLElement;
|
||||||
private is_drawing = false;
|
private is_drawing = false;
|
||||||
private active_box: AnnotationBox;
|
private active_box: AnnotationBox;
|
||||||
private active_label: string;
|
|
||||||
private boxes: Array<AnnotationBox> = [];
|
private boxes: Array<AnnotationBox> = [];
|
||||||
private display_element: HTMLElement;
|
private display_element: HTMLElement;
|
||||||
private deselect_all_flag = true;
|
private deselect_all_flag = true;
|
||||||
|
private categories = [];
|
||||||
|
private active_category = { id: null, label: "?", color: "#444" };
|
||||||
|
private palette: Array<string>;
|
||||||
|
|
||||||
constructor(private jsPsych: JsPsych) {}
|
constructor(private jsPsych: JsPsych) {}
|
||||||
|
|
||||||
trial(display_element: HTMLElement, trial: TrialType<Info>) {
|
trial(display_element: HTMLElement, trial: TrialType<Info>) {
|
||||||
this.display_element = display_element;
|
this.display_element = display_element;
|
||||||
|
this.palette = trial.colors;
|
||||||
|
|
||||||
|
for (let i = 0; i < trial.labels.length; i++) {
|
||||||
|
this.categories.push({
|
||||||
|
id: i,
|
||||||
|
color: trial.colors[i],
|
||||||
|
label: trial.labels[i],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.add_css();
|
this.add_css();
|
||||||
this.renderDisplay(trial);
|
this.renderDisplay(trial);
|
||||||
@ -75,14 +100,14 @@ class ImageTextAnnotationPlugin implements JsPsychPlugin<Info> {
|
|||||||
</div>
|
</div>
|
||||||
<div id='annotation-options'>
|
<div id='annotation-options'>
|
||||||
`;
|
`;
|
||||||
let i = 1;
|
let i = 0;
|
||||||
for (const l of trial.labels) {
|
for (const l of trial.labels) {
|
||||||
html += `<div><input type="radio" id="opt${i}" name="annotate_label" value="${l}"><label for="opt${i}">${l}</label></div>`;
|
html += `<div style="--main-color:${trial.colors[i]}"><input type="radio" id="opt${i}" name="annotate_label" value="${l}"><label for="opt${i}">${l}</label></div>`;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
html += `
|
// html += `
|
||||||
<div><input type="radio" id="opt${i}" name="annotate_label" value=""><label for="opt${i}"><input id="opt${i}_text" type="text"></label></div>
|
// <div><input type="radio" id="opt${i}" name="annotate_label" value=""><label for="opt${i}"><input id="opt${i}_text" type="text"></label></div>
|
||||||
`;
|
// `;
|
||||||
html += `
|
html += `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -91,6 +116,8 @@ class ImageTextAnnotationPlugin implements JsPsychPlugin<Info> {
|
|||||||
|
|
||||||
this.display_element.innerHTML = html;
|
this.display_element.innerHTML = html;
|
||||||
|
|
||||||
|
this.add_new_label();
|
||||||
|
|
||||||
this.img_container = this.display_element.querySelector("#annotated-image-container");
|
this.img_container = this.display_element.querySelector("#annotated-image-container");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,13 +135,6 @@ class ImageTextAnnotationPlugin implements JsPsychPlugin<Info> {
|
|||||||
this.deselect_all_flag = true;
|
this.deselect_all_flag = true;
|
||||||
});
|
});
|
||||||
document.addEventListener("click", this.deselect_all);
|
document.addEventListener("click", this.deselect_all);
|
||||||
|
|
||||||
this.display_element
|
|
||||||
.querySelector('input[type="text"]')
|
|
||||||
.addEventListener("change", this.add_new_label);
|
|
||||||
this.display_element
|
|
||||||
.querySelector('input[type="text"]')
|
|
||||||
.addEventListener("change", this.update_labels);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private add_css() {
|
private add_css() {
|
||||||
@ -135,21 +155,28 @@ class ImageTextAnnotationPlugin implements JsPsychPlugin<Info> {
|
|||||||
|
|
||||||
#jspsych-annotation-display label {
|
#jspsych-annotation-display label {
|
||||||
border-radius: 0.75em;
|
border-radius: 0.75em;
|
||||||
background-color: green;
|
background-color: var(--main-color);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0.35em 0.5em;
|
padding: 0.35em 0.5em;
|
||||||
margin: 0.25em 0em;
|
margin-bottom: 0.25em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
|
transition: margin-right 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
#jspsych-annotation-display label::before {
|
#jspsych-annotation-display input[type="radio"] + label::before {
|
||||||
content: url('data:image/svg+xml; utf8, <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="white"><path d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/></svg>');
|
content: url('data:image/svg+xml; utf8, <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="white"><path d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/></svg>');
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding-right: 0.5em;
|
padding-right: 0.5em;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 0.15em;
|
top: 0.15em;
|
||||||
|
transform: scale(0);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#jspsych-annotation-display input[type="radio"]:checked + label::before {
|
||||||
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#jspsych-annotation-display label input[type="text"] {
|
#jspsych-annotation-display label input[type="text"] {
|
||||||
@ -172,10 +199,6 @@ class ImageTextAnnotationPlugin implements JsPsychPlugin<Info> {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#jspsych-annotation-display input[type="radio"]:checked + label {
|
|
||||||
background-color: grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jspsych-annotation-display #annotated-image-container {
|
#jspsych-annotation-display #annotated-image-container {
|
||||||
cursor: crosshair;
|
cursor: crosshair;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -187,11 +210,11 @@ class ImageTextAnnotationPlugin implements JsPsychPlugin<Info> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#jspsych-annotation-display #annotated-image-container .annotation-box {
|
#jspsych-annotation-display #annotated-image-container .annotation-box {
|
||||||
border: 1px solid green;
|
border: 1px solid var(--main-color);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
color:green;
|
color: var(--main-color);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: #00800040;
|
background-color: var(--very-transparent-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#jspsych-annotation-display #annotated-image-container .annotation-box:hover {
|
#jspsych-annotation-display #annotated-image-container .annotation-box:hover {
|
||||||
@ -204,9 +227,9 @@ class ImageTextAnnotationPlugin implements JsPsychPlugin<Info> {
|
|||||||
text-align:left;
|
text-align:left;
|
||||||
line-height:1em;
|
line-height:1em;
|
||||||
color: white;
|
color: white;
|
||||||
background-color: green;
|
background-color: var(--main-color);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: 1px solid green;
|
border: 1px solid var(--main-color);
|
||||||
box-shadow: 1px 1px 3px rgba(0,0,0,0.5);
|
box-shadow: 1px 1px 3px rgba(0,0,0,0.5);
|
||||||
position:absolute;
|
position:absolute;
|
||||||
top:2px;
|
top:2px;
|
||||||
@ -262,8 +285,8 @@ class ImageTextAnnotationPlugin implements JsPsychPlugin<Info> {
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
width: 4px;
|
width: 4px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
background-color: #00800080;
|
background-color: var(--transparent-color);
|
||||||
border: 1px solid #00800080;
|
border: 1px solid var(--transparent-color);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
transition: height 0.25s, width 0.25s, left 0.25s, top 0.25s, right 0.25s, bottom 0.25s;
|
transition: height 0.25s, width 0.25s, left 0.25s, top 0.25s, right 0.25s, bottom 0.25s;
|
||||||
@ -368,7 +391,7 @@ class ImageTextAnnotationPlugin implements JsPsychPlugin<Info> {
|
|||||||
if (this.is_drawing) {
|
if (this.is_drawing) {
|
||||||
this.active_box.finishDrawing();
|
this.active_box.finishDrawing();
|
||||||
|
|
||||||
this.active_box.setLabel(this.active_label);
|
this.active_box.setCategory(this.active_category);
|
||||||
|
|
||||||
this.active_box.select();
|
this.active_box.select();
|
||||||
|
|
||||||
@ -405,10 +428,10 @@ class ImageTextAnnotationPlugin implements JsPsychPlugin<Info> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handle_radio_change(e) {
|
private handle_radio_change(e) {
|
||||||
this.active_label = (e.target as HTMLFormElement).value;
|
this.active_category = this.categories[(e.target as HTMLFormElement).id.substring(3, 4)];
|
||||||
for (const b of this.boxes) {
|
for (const b of this.boxes) {
|
||||||
if (b.isSelected()) {
|
if (b.isSelected()) {
|
||||||
b.setLabel(this.active_label);
|
b.setCategory(this.active_category);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -421,25 +444,37 @@ class ImageTextAnnotationPlugin implements JsPsychPlugin<Info> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private add_new_label(e) {
|
private add_new_label(e?) {
|
||||||
const container = this.display_element.querySelector("#annotation-options");
|
const container = this.display_element.querySelector("#annotation-options");
|
||||||
const options = container.querySelectorAll('input[type="radio"]').length;
|
const category_id = this.categories.length;
|
||||||
|
|
||||||
|
this.categories.push({
|
||||||
|
id: category_id,
|
||||||
|
label: "",
|
||||||
|
color: this.palette[category_id],
|
||||||
|
});
|
||||||
|
|
||||||
const html = `
|
const html = `
|
||||||
<div><input type="radio" id="opt${options + 1}" name="annotate_label" value=""><label for="${
|
<div style="--main-color: ${this.palette[category_id]}"><input type="radio" id="opt${category_id}" name="annotate_label" value=""><label for="opt${category_id}"><input id="opt${category_id}_text" type="text"></label></div>
|
||||||
options + 1
|
|
||||||
}"><input id="opt${options + 1}_text" type="text"></label></div>
|
|
||||||
`;
|
`;
|
||||||
container.insertAdjacentHTML("beforeend", html);
|
container.insertAdjacentHTML("beforeend", html);
|
||||||
container
|
container
|
||||||
.querySelector(`#opt${options + 1}_text`)
|
.querySelector(`#opt${category_id}_text`)
|
||||||
.addEventListener("change", this.add_new_label);
|
.addEventListener("change", this.add_new_label);
|
||||||
container
|
container
|
||||||
.querySelector(`#opt${options + 1}_text`)
|
.querySelector(`#opt${category_id}_text`)
|
||||||
.addEventListener("change", this.update_labels);
|
.addEventListener("change", this.update_labels);
|
||||||
|
container.querySelector(`#opt${category_id}_text`).addEventListener("click", (e) => {
|
||||||
|
(e.target as HTMLFormElement).parentElement.parentElement.querySelector("input").checked =
|
||||||
|
true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (e) {
|
||||||
e.target.removeEventListener("change", this.add_new_label);
|
e.target.removeEventListener("change", this.add_new_label);
|
||||||
|
}
|
||||||
|
|
||||||
container
|
container
|
||||||
.querySelector(`#opt${options + 1}`)
|
.querySelector(`#opt${category_id}`)
|
||||||
.addEventListener("change", this.handle_radio_change);
|
.addEventListener("change", this.handle_radio_change);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,6 +511,8 @@ class AnnotationBox {
|
|||||||
private selected = false;
|
private selected = false;
|
||||||
private plugin: ImageTextAnnotationPlugin;
|
private plugin: ImageTextAnnotationPlugin;
|
||||||
private modifiable = true;
|
private modifiable = true;
|
||||||
|
private color = "#444444";
|
||||||
|
private category = null;
|
||||||
|
|
||||||
constructor(x, y, box_list, container, plugin) {
|
constructor(x, y, box_list, container, plugin) {
|
||||||
autoBind(this);
|
autoBind(this);
|
||||||
@ -493,6 +530,8 @@ class AnnotationBox {
|
|||||||
|
|
||||||
this.element = el;
|
this.element = el;
|
||||||
|
|
||||||
|
this.setColor(this.color);
|
||||||
|
|
||||||
this.container.appendChild(this.element);
|
this.container.appendChild(this.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,6 +544,18 @@ class AnnotationBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCategory(category) {
|
||||||
|
this.category = category.id;
|
||||||
|
this.setLabel(category.label);
|
||||||
|
this.setColor(category.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
setColor(color) {
|
||||||
|
this.element.style.setProperty("--main-color", color);
|
||||||
|
this.element.style.setProperty("--transparent-color", color + "80");
|
||||||
|
this.element.style.setProperty("--very-transparent-color", color + "40");
|
||||||
|
}
|
||||||
|
|
||||||
setLabel(label) {
|
setLabel(label) {
|
||||||
if (label) {
|
if (label) {
|
||||||
this.label = label;
|
this.label = label;
|
||||||
|
Loading…
Reference in New Issue
Block a user