Fix misaligned image occlusions (#2512)
* Cloze styling is not required in I/O notetype * Use raw string for IO template * Rename to notetype.css and use more specific ids * Move internal i/o styling into runtime Storing it in the notetype makes it difficult to make changes, and makes it easier for the user to break. * Fix misaligned occlusions At larger screen sizes, the canvas was not increasing above its configured size, so it ended up being placed top center instead of expanding to fit the entire container area. To resolve this, both the image and canvas are forced to the container size, and the container is constrained to the size of the viewport, with the same aspect ratio as the image. Closes #2492
This commit is contained in:
parent
581f82c589
commit
7686cb8de8
@ -394,7 +394,7 @@ fn build_and_check_editor(build: &mut Build) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_and_check_reviewer(build: &mut Build) -> Result<()> {
|
fn build_and_check_reviewer(build: &mut Build) -> Result<()> {
|
||||||
let reviewer_deps = inputs![":ts:lib", glob!("ts/reviewer/**")];
|
let reviewer_deps = inputs![":ts:lib", glob!("ts/reviewer/**"),];
|
||||||
build.add(
|
build.add(
|
||||||
"ts:reviewer:reviewer.js",
|
"ts:reviewer:reviewer.js",
|
||||||
EsbuildScript {
|
EsbuildScript {
|
||||||
@ -410,7 +410,7 @@ fn build_and_check_reviewer(build: &mut Build) -> Result<()> {
|
|||||||
CompileSass {
|
CompileSass {
|
||||||
input: inputs!["ts/reviewer/reviewer.scss"],
|
input: inputs!["ts/reviewer/reviewer.scss"],
|
||||||
output: "ts/reviewer/reviewer.css",
|
output: "ts/reviewer/reviewer.css",
|
||||||
deps: ":sass".into(),
|
deps: inputs![":sass", "ts/image-occlusion/review.scss"],
|
||||||
load_paths: vec!["."],
|
load_paths: vec!["."],
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
.image-occlusion-canvas {
|
|
||||||
--inactive-shape-color: #ffeba2;
|
|
||||||
--active-shape-color: #ff8e8e;
|
|
||||||
--inactive-shape-border: 1px #212121;
|
|
||||||
--active-shape-border: 1px #212121;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
font-family: arial;
|
|
||||||
font-size: 20px;
|
|
||||||
text-align: center;
|
|
||||||
color: black;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cloze {
|
|
||||||
font-weight: bold;
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nightMode .cloze {
|
|
||||||
color: lightblue;
|
|
||||||
}
|
|
||||||
|
|
||||||
#container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#canvas {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, 0);
|
|
||||||
}
|
|
@ -47,10 +47,7 @@ impl Collection {
|
|||||||
let mgr = MediaManager::new(&self.media_folder, &self.media_db)?;
|
let mgr = MediaManager::new(&self.media_folder, &self.media_db)?;
|
||||||
let actual_image_name_after_adding = mgr.add_file(&image_filename, &image_bytes)?;
|
let actual_image_name_after_adding = mgr.add_file(&image_filename, &image_bytes)?;
|
||||||
|
|
||||||
let image_tag = format!(
|
let image_tag = format!(r#"<img src="{}">"#, &actual_image_name_after_adding);
|
||||||
r#"<img id="img" src="{}">"#,
|
|
||||||
&actual_image_name_after_adding
|
|
||||||
);
|
|
||||||
|
|
||||||
let current_deck = self.get_current_deck()?;
|
let current_deck = self.get_current_deck()?;
|
||||||
self.transact(Op::ImageOcclusion, |col| {
|
self.transact(Op::ImageOcclusion, |col| {
|
||||||
|
14
rslib/src/image_occlusion/notetype.css
Normal file
14
rslib/src/image_occlusion/notetype.css
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#image-occlusion-canvas {
|
||||||
|
--inactive-shape-color: #ffeba2;
|
||||||
|
--active-shape-color: #ff8e8e;
|
||||||
|
--inactive-shape-border: 1px #212121;
|
||||||
|
--active-shape-border: 1px #212121;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
font-family: arial;
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: black;
|
||||||
|
background-color: white;
|
||||||
|
}
|
@ -51,7 +51,7 @@ impl Collection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn image_occlusion_notetype(tr: &I18n) -> Notetype {
|
pub(crate) fn image_occlusion_notetype(tr: &I18n) -> Notetype {
|
||||||
const IMAGE_CLOZE_CSS: &str = include_str!("image_occlusion_styling.css");
|
const IMAGE_CLOZE_CSS: &str = include_str!("notetype.css");
|
||||||
let mut nt = empty_stock(
|
let mut nt = empty_stock(
|
||||||
NotetypeKind::Cloze,
|
NotetypeKind::Cloze,
|
||||||
OriginalStockKind::ImageOcclusion,
|
OriginalStockKind::ImageOcclusion,
|
||||||
@ -71,31 +71,29 @@ pub(crate) fn image_occlusion_notetype(tr: &I18n) -> Notetype {
|
|||||||
|
|
||||||
let err_loading = tr.notetypes_error_loading_image_occlusion();
|
let err_loading = tr.notetypes_error_loading_image_occlusion();
|
||||||
let qfmt = format!(
|
let qfmt = format!(
|
||||||
"\
|
r#"{{{{#{header}}}}}<div>{{{{{header}}}}}</div>{{{{/{header}}}}}
|
||||||
{{{{#{header}}}}}<div>{{{{{header}}}}}</div>{{{{/{header}}}}}
|
<div style="display: none">{{{{cloze:{occlusion}}}}}</div>
|
||||||
<div style=\"display: none\">{{{{cloze:{occlusion}}}}}</div>
|
<div id="err"></div>
|
||||||
<div id=\"err\"></div>
|
<div id="image-occlusion-container">
|
||||||
<div id=container>
|
|
||||||
{{{{{image}}}}}
|
{{{{{image}}}}}
|
||||||
<canvas id=\"canvas\" class=\"image-occlusion-canvas\"></canvas>
|
<canvas id="image-occlusion-canvas"></canvas>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
try {{
|
try {{
|
||||||
anki.setupImageCloze();
|
anki.setupImageCloze();
|
||||||
}} catch (exc) {{
|
}} catch (exc) {{
|
||||||
document.getElementById(\"err\").innerHTML = `{err_loading}<br><br>${{exc}}`;
|
document.getElementById("err").innerHTML = `{err_loading}<br><br>${{exc}}`;
|
||||||
}}
|
}}
|
||||||
</script>
|
</script>
|
||||||
"
|
"#
|
||||||
);
|
);
|
||||||
|
|
||||||
let toggle_masks = tr.notetypes_toggle_masks();
|
let toggle_masks = tr.notetypes_toggle_masks();
|
||||||
let afmt = format!(
|
let afmt = format!(
|
||||||
"\
|
r#"{qfmt}
|
||||||
{qfmt}
|
<div><button id="toggle">{toggle_masks}</button></div>
|
||||||
<div><button id=\"toggle\">{toggle_masks}</button></div>
|
|
||||||
{{{{#{back_extra}}}}}<div>{{{{{back_extra}}}}}</div>{{{{/{back_extra}}}}}
|
{{{{#{back_extra}}}}}<div>{{{{{back_extra}}}}}</div>{{{{/{back_extra}}}}}
|
||||||
",
|
"#,
|
||||||
);
|
);
|
||||||
nt.add_template(nt.name.clone(), qfmt, afmt);
|
nt.add_template(nt.name.clone(), qfmt, afmt);
|
||||||
nt
|
nt
|
||||||
|
28
ts/image-occlusion/review.scss
Normal file
28
ts/image-occlusion/review.scss
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#image-occlusion-container {
|
||||||
|
position: relative;
|
||||||
|
// if height-constrained, ensure container is centered
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
// allow for 20px margin on html element, or short windows can truncate
|
||||||
|
// image
|
||||||
|
max-height: calc(95vh - 40px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#image-occlusion-container img {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
// remove the default image limits, as we rely on container
|
||||||
|
max-width: unset;
|
||||||
|
max-height: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#image-occlusion-canvas {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
@ -12,17 +12,14 @@ export function setupImageCloze(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setupImageClozeInner(): void {
|
function setupImageClozeInner(): void {
|
||||||
const canvas = document.querySelector("canvas") as HTMLCanvasElement | null;
|
const canvas = document.querySelector("#image-occlusion-canvas") as HTMLCanvasElement | null;
|
||||||
if (canvas == null) {
|
if (canvas == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.style.maxWidth = "100%";
|
|
||||||
canvas.style.maxHeight = "95vh";
|
|
||||||
|
|
||||||
const ctx: CanvasRenderingContext2D = canvas.getContext("2d")!;
|
const ctx: CanvasRenderingContext2D = canvas.getContext("2d")!;
|
||||||
const container = document.getElementById("container") as HTMLDivElement;
|
const container = document.getElementById("image-occlusion-container") as HTMLDivElement;
|
||||||
const image = document.getElementById("img") as HTMLImageElement;
|
const image = document.querySelector("#image-occlusion-container img") as HTMLImageElement;
|
||||||
if (image == null) {
|
if (image == null) {
|
||||||
container.innerText = tr.notetypeErrorNoImageToShow();
|
container.innerText = tr.notetypeErrorNoImageToShow();
|
||||||
return;
|
return;
|
||||||
@ -32,8 +29,8 @@ function setupImageClozeInner(): void {
|
|||||||
canvas.width = size.width;
|
canvas.width = size.width;
|
||||||
canvas.height = size.height;
|
canvas.height = size.height;
|
||||||
|
|
||||||
// set height for div container (used 'relative' in css)
|
// Enforce aspect ratio of image
|
||||||
container.style.height = `${image.height}px`;
|
container.style.aspectRatio = `${size.width / size.height}`;
|
||||||
|
|
||||||
// setup button for toggle image occlusion
|
// setup button for toggle image occlusion
|
||||||
const button = document.getElementById("toggle");
|
const button = document.getElementById("toggle");
|
||||||
@ -151,7 +148,7 @@ function getShapeProperty(): {
|
|||||||
activeBorder: { width: number; color: string };
|
activeBorder: { width: number; color: string };
|
||||||
inActiveBorder: { width: number; color: string };
|
inActiveBorder: { width: number; color: string };
|
||||||
} {
|
} {
|
||||||
const canvas = document.getElementById("canvas");
|
const canvas = document.getElementById("image-occlusion-canvas");
|
||||||
const computedStyle = window.getComputedStyle(canvas!);
|
const computedStyle = window.getComputedStyle(canvas!);
|
||||||
// it may throw error if the css variable is not defined
|
// it may throw error if the css variable is not defined
|
||||||
try {
|
try {
|
||||||
@ -199,7 +196,7 @@ function getShapeProperty(): {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const toggleMasks = (): void => {
|
const toggleMasks = (): void => {
|
||||||
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
|
const canvas = document.getElementById("image-occlusion-canvas") as HTMLCanvasElement;
|
||||||
const display = canvas.style.display;
|
const display = canvas.style.display;
|
||||||
if (display === "none") {
|
if (display === "none") {
|
||||||
canvas.style.display = "unset";
|
canvas.style.display = "unset";
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
|
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
|
||||||
|
|
||||||
@use "sass/vars";
|
@use "sass/vars";
|
||||||
|
@use "ts/image-occlusion/review";
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
background-color: vars.palette(darkgray, 0);
|
background-color: vars.palette(darkgray, 0);
|
||||||
|
Loading…
Reference in New Issue
Block a user