Add Image Diff options in Pull Request Diff view (#14450)
	
		
	
				
					
				
			Implemented GitHub style image difftokarchuk/v1.17
							parent
							
								
									42118c6bc8
								
							
						
					
					
						commit
						904a26c57c
					
				@ -1,79 +1,109 @@ | 
				
			|||||||
{{ $imagePathOld := printf "%s/%s" .root.BeforeRawPath (EscapePound .file.OldName)  }} | 
					{{ $imagePathOld := printf "%s/%s" .root.BeforeRawPath (EscapePound .file.OldName)  }} | 
				
			||||||
{{ $imagePathNew := printf "%s/%s" .root.RawPath (EscapePound .file.Name)  }} | 
					{{ $imagePathNew := printf "%s/%s" .root.RawPath (EscapePound .file.Name)  }} | 
				
			||||||
 | 
					 | 
				
			||||||
<tr> | 
					 | 
				
			||||||
 	<th class="halfwidth center pl-3 pr-2"> | 
					 | 
				
			||||||
 		{{.root.i18n.Tr "repo.diff.file_before"}} | 
					 | 
				
			||||||
 	</th> | 
					 | 
				
			||||||
 	<th class="halfwidth center pl-2 pr-3"> | 
					 | 
				
			||||||
 		{{.root.i18n.Tr "repo.diff.file_after"}} | 
					 | 
				
			||||||
 	</th> | 
					 | 
				
			||||||
</tr> | 
					 | 
				
			||||||
<tr> | 
					 | 
				
			||||||
 	<td class="halfwidth center pl-3 pr-2"> | 
					 | 
				
			||||||
 	    {{if or .file.IsDeleted (not .file.IsCreated)}} | 
					 | 
				
			||||||
            <a href="{{$imagePathOld}}" target="_blank"> | 
					 | 
				
			||||||
                <img src="{{$imagePathOld}}" class="border red" /> | 
					 | 
				
			||||||
            </a> | 
					 | 
				
			||||||
 	    {{end}} | 
					 | 
				
			||||||
 	</td> | 
					 | 
				
			||||||
 	<td class="halfwidth center pl-2 pr-3"> | 
					 | 
				
			||||||
 	    {{if or .file.IsCreated (not .file.IsDeleted)}} | 
					 | 
				
			||||||
			<a href="{{$imagePathNew}}" target="_blank"> | 
					 | 
				
			||||||
				<img src="{{$imagePathNew}}" class="border green" /> | 
					 | 
				
			||||||
			</a> | 
					 | 
				
			||||||
 	    {{end}} | 
					 | 
				
			||||||
 	</td> | 
					 | 
				
			||||||
</tr> | 
					 | 
				
			||||||
{{ $imageInfoBase := (call .root.ImageInfoBase .file.OldName) }} | 
					{{ $imageInfoBase := (call .root.ImageInfoBase .file.OldName) }} | 
				
			||||||
{{ $imageInfoHead := (call .root.ImageInfo .file.Name) }} | 
					{{ $imageInfoHead := (call .root.ImageInfo .file.Name) }} | 
				
			||||||
{{if or $imageInfoBase $imageInfoHead }} | 
					{{if or $imageInfoBase $imageInfoHead}} | 
				
			||||||
<tr> | 
					<tr> | 
				
			||||||
 	<td class="halfwidth center pl-3 pr-2"> | 
						<td colspan="2"> | 
				
			||||||
 	{{if $imageInfoBase }} | 
							<div class="image-diff" data-path-before="{{$imagePathOld}}" data-path-after="{{$imagePathNew}}"> | 
				
			||||||
 		{{ $classWidth := "" }} | 
								<div class="ui secondary pointing tabular top attached borderless menu stackable new-menu"> | 
				
			||||||
 		{{ $classHeight := "" }} | 
									<div class="new-menu-inner"> | 
				
			||||||
 		{{ $classByteSize := "" }} | 
										<a class="item active" data-tab="diff-side-by-side">{{.root.i18n.Tr "repo.diff.image.side_by_side"}}</a> | 
				
			||||||
 		{{if $imageInfoHead}} | 
										{{if and $imageInfoBase $imageInfoHead}} | 
				
			||||||
			{{if not (eq $imageInfoBase.Width $imageInfoHead.Width)}} | 
										<a class="item" data-tab="diff-swipe">{{.root.i18n.Tr "repo.diff.image.swipe"}}</a> | 
				
			||||||
				{{ $classWidth = "red" }} | 
										<a class="item" data-tab="diff-overlay">{{.root.i18n.Tr "repo.diff.image.overlay"}}</a> | 
				
			||||||
			{{end}} | 
										{{end}} | 
				
			||||||
			{{if not (eq $imageInfoBase.Height $imageInfoHead.Height)}} | 
									</div> | 
				
			||||||
				{{ $classHeight = "red" }} | 
								</div> | 
				
			||||||
			{{end}} | 
								<div class="hide"> | 
				
			||||||
			{{if not (eq $imageInfoBase.ByteSize $imageInfoHead.ByteSize)}} | 
									<div class="ui bottom attached tab image-diff-container active" data-tab="diff-side-by-side"> | 
				
			||||||
				{{ $classByteSize = "red" }} | 
										<div class="diff-side-by-side"> | 
				
			||||||
			{{end}} | 
											{{if $imageInfoBase }} | 
				
			||||||
 		{{end}} | 
											<span class="side"> | 
				
			||||||
 		{{.root.i18n.Tr "repo.diff.file_image_width"}}: <span class="text {{$classWidth}}">{{$imageInfoBase.Width}}</span> | 
												<p class="side-header">{{.root.i18n.Tr "repo.diff.file_before"}}</p> | 
				
			||||||
 		 |  | 
												<span class="before-container"><img class="image-before" /></span> | 
				
			||||||
 	    {{.root.i18n.Tr "repo.diff.file_image_height"}}: <span class="text {{$classHeight}}">{{$imageInfoBase.Height}}</span> | 
												<p> | 
				
			||||||
 		 |  | 
													{{ $classWidth := "" }} | 
				
			||||||
 	    {{.root.i18n.Tr "repo.diff.file_byte_size"}}: <span class="text {{$classByteSize}}">{{FileSize $imageInfoBase.ByteSize}}</span> | 
													{{ $classHeight := "" }} | 
				
			||||||
 	{{end}} | 
													{{ $classByteSize := "" }} | 
				
			||||||
 	</td> | 
													{{if $imageInfoHead}} | 
				
			||||||
 	<td class="halfwidth center pl-2 pr-3"> | 
														{{if not (eq $imageInfoBase.Width $imageInfoHead.Width)}} | 
				
			||||||
 	{{if $imageInfoHead }} | 
															{{ $classWidth = "red" }} | 
				
			||||||
 		{{ $classWidth := "" }} | 
														{{end}} | 
				
			||||||
 		{{ $classHeight := "" }} | 
														{{if not (eq $imageInfoBase.Height $imageInfoHead.Height)}} | 
				
			||||||
 		{{ $classByteSize := "" }} | 
															{{ $classHeight = "red" }} | 
				
			||||||
 		{{if $imageInfoBase}} | 
														{{end}} | 
				
			||||||
			{{if not (eq $imageInfoBase.Width $imageInfoHead.Width)}} | 
														{{if not (eq $imageInfoBase.ByteSize $imageInfoHead.ByteSize)}} | 
				
			||||||
				{{ $classWidth = "green" }} | 
															{{ $classByteSize = "red" }} | 
				
			||||||
			{{end}} | 
														{{end}} | 
				
			||||||
			{{if not (eq $imageInfoBase.Height $imageInfoHead.Height)}} | 
													{{end}} | 
				
			||||||
				{{ $classHeight = "green" }} | 
													{{.root.i18n.Tr "repo.diff.file_image_width"}}: <span class="text {{$classWidth}}">{{$imageInfoBase.Width}}</span> | 
				
			||||||
			{{end}} | 
													 |  | 
				
			||||||
			{{if not (eq $imageInfoBase.ByteSize $imageInfoHead.ByteSize)}} | 
													{{.root.i18n.Tr "repo.diff.file_image_height"}}: <span class="text {{$classHeight}}">{{$imageInfoBase.Height}}</span> | 
				
			||||||
				{{ $classByteSize = "green" }} | 
													 |  | 
				
			||||||
			{{end}} | 
													{{.root.i18n.Tr "repo.diff.file_byte_size"}}: <span class="text {{$classByteSize}}">{{FileSize $imageInfoBase.ByteSize}}</span> | 
				
			||||||
 		{{end}} | 
												</p> | 
				
			||||||
 		{{.root.i18n.Tr "repo.diff.file_image_width"}}: <span class="text {{$classWidth}}">{{$imageInfoHead.Width}}</span> | 
											</span> | 
				
			||||||
 		 |  | 
											{{end}} | 
				
			||||||
 	    {{.root.i18n.Tr "repo.diff.file_image_height"}}: <span class="text {{$classHeight}}">{{$imageInfoHead.Height}}</span> | 
											{{if $imageInfoHead }} | 
				
			||||||
 		 |  | 
											<span class="side"> | 
				
			||||||
 	    {{.root.i18n.Tr "repo.diff.file_byte_size"}}: <span class="text {{$classByteSize}}">{{FileSize $imageInfoHead.ByteSize}}</span> | 
												<p class="side-header">{{.root.i18n.Tr "repo.diff.file_after"}}</p> | 
				
			||||||
 	{{end}} | 
												<span class="after-container"><img class="image-after" /></span> | 
				
			||||||
 	</td> | 
												<p> | 
				
			||||||
 </tr> | 
													{{ $classWidth := "" }} | 
				
			||||||
 | 
													{{ $classHeight := "" }} | 
				
			||||||
 | 
													{{ $classByteSize := "" }} | 
				
			||||||
 | 
													{{if $imageInfoBase}} | 
				
			||||||
 | 
														{{if not (eq $imageInfoBase.Width $imageInfoHead.Width)}} | 
				
			||||||
 | 
															{{ $classWidth = "green" }} | 
				
			||||||
 | 
														{{end}} | 
				
			||||||
 | 
														{{if not (eq $imageInfoBase.Height $imageInfoHead.Height)}} | 
				
			||||||
 | 
															{{ $classHeight = "green" }} | 
				
			||||||
 | 
														{{end}} | 
				
			||||||
 | 
														{{if not (eq $imageInfoBase.ByteSize $imageInfoHead.ByteSize)}} | 
				
			||||||
 | 
															{{ $classByteSize = "green" }} | 
				
			||||||
 | 
														{{end}} | 
				
			||||||
 | 
													{{end}} | 
				
			||||||
 | 
													{{.root.i18n.Tr "repo.diff.file_image_width"}}: <span class="text {{$classWidth}}">{{$imageInfoHead.Width}}</span> | 
				
			||||||
 | 
													 |  | 
				
			||||||
 | 
													{{.root.i18n.Tr "repo.diff.file_image_height"}}: <span class="text {{$classHeight}}">{{$imageInfoHead.Height}}</span> | 
				
			||||||
 | 
													 |  | 
				
			||||||
 | 
													{{.root.i18n.Tr "repo.diff.file_byte_size"}}: <span class="text {{$classByteSize}}">{{FileSize $imageInfoHead.ByteSize}}</span> | 
				
			||||||
 | 
												</p> | 
				
			||||||
 | 
											</span> | 
				
			||||||
 | 
											{{end}} | 
				
			||||||
 | 
										</div> | 
				
			||||||
 | 
									</div> | 
				
			||||||
 | 
									{{if and $imageInfoBase $imageInfoHead}} | 
				
			||||||
 | 
									<div class="ui bottom attached tab image-diff-container" data-tab="diff-swipe"> | 
				
			||||||
 | 
										<div class="diff-swipe"> | 
				
			||||||
 | 
											<div class="swipe-frame"> | 
				
			||||||
 | 
												<span class="before-container"><img class="image-before" /></span> | 
				
			||||||
 | 
												<span class="swipe-container"> | 
				
			||||||
 | 
													<span class="after-container"><img class="image-after" /></span> | 
				
			||||||
 | 
												</span> | 
				
			||||||
 | 
												<span class="swipe-bar"> | 
				
			||||||
 | 
													<span class="handle top-handle"></span> | 
				
			||||||
 | 
													<span class="handle bottom-handle"></span> | 
				
			||||||
 | 
												</span> | 
				
			||||||
 | 
											</div> | 
				
			||||||
 | 
										</div> | 
				
			||||||
 | 
									</div> | 
				
			||||||
 | 
									<div class="ui bottom attached tab image-diff-container" data-tab="diff-overlay"> | 
				
			||||||
 | 
										<div class="diff-overlay"> | 
				
			||||||
 | 
											<div class="overlay-frame"> | 
				
			||||||
 | 
												<div class="ui centered"> | 
				
			||||||
 | 
													<input type="range" min="0" max="100" value="50" /> | 
				
			||||||
 | 
												</div> | 
				
			||||||
 | 
												<span class="before-container"><img class="image-before"/></span> | 
				
			||||||
 | 
												<span class="after-container"><img class="image-after" /></span> | 
				
			||||||
 | 
											</div> | 
				
			||||||
 | 
										</div> | 
				
			||||||
 | 
									</div> | 
				
			||||||
 | 
									{{end}} | 
				
			||||||
 | 
								</div> | 
				
			||||||
 | 
								<div class="ui active centered inline loader"></div> | 
				
			||||||
 | 
							</div> | 
				
			||||||
 | 
						</td> | 
				
			||||||
 | 
					</tr> | 
				
			||||||
{{end}} | 
					{{end}} | 
				
			||||||
@ -0,0 +1,206 @@ | 
				
			|||||||
 | 
					export default async function initImageDiff() { | 
				
			||||||
 | 
					  function createContext(image1, image2) { | 
				
			||||||
 | 
					    const size1 = { | 
				
			||||||
 | 
					      width: image1 && image1.width || 0, | 
				
			||||||
 | 
					      height: image1 && image1.height || 0 | 
				
			||||||
 | 
					    }; | 
				
			||||||
 | 
					    const size2 = { | 
				
			||||||
 | 
					      width: image2 && image2.width || 0, | 
				
			||||||
 | 
					      height: image2 && image2.height || 0 | 
				
			||||||
 | 
					    }; | 
				
			||||||
 | 
					    const max = { | 
				
			||||||
 | 
					      width: Math.max(size2.width, size1.width), | 
				
			||||||
 | 
					      height: Math.max(size2.height, size1.height) | 
				
			||||||
 | 
					    }; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return { | 
				
			||||||
 | 
					      image1: $(image1), | 
				
			||||||
 | 
					      image2: $(image2), | 
				
			||||||
 | 
					      size1, | 
				
			||||||
 | 
					      size2, | 
				
			||||||
 | 
					      max, | 
				
			||||||
 | 
					      ratio: [ | 
				
			||||||
 | 
					        Math.floor(max.width - size1.width) / 2, | 
				
			||||||
 | 
					        Math.floor(max.height - size1.height) / 2, | 
				
			||||||
 | 
					        Math.floor(max.width - size2.width) / 2, | 
				
			||||||
 | 
					        Math.floor(max.height - size2.height) / 2 | 
				
			||||||
 | 
					      ] | 
				
			||||||
 | 
					    }; | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  $('.image-diff').each(function() { | 
				
			||||||
 | 
					    const $container = $(this); | 
				
			||||||
 | 
					    const pathAfter = $container.data('path-after'); | 
				
			||||||
 | 
					    const pathBefore = $container.data('path-before'); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const imageInfos = [{ | 
				
			||||||
 | 
					      loaded: false, | 
				
			||||||
 | 
					      path: pathAfter, | 
				
			||||||
 | 
					      $image: $container.find('img.image-after') | 
				
			||||||
 | 
					    }, { | 
				
			||||||
 | 
					      loaded: false, | 
				
			||||||
 | 
					      path: pathBefore, | 
				
			||||||
 | 
					      $image: $container.find('img.image-before') | 
				
			||||||
 | 
					    }]; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const info of imageInfos) { | 
				
			||||||
 | 
					      if (info.$image.length > 0) { | 
				
			||||||
 | 
					        info.$image.on('load', () => { | 
				
			||||||
 | 
					          info.loaded = true; | 
				
			||||||
 | 
					          setReadyIfLoaded(); | 
				
			||||||
 | 
					        }); | 
				
			||||||
 | 
					        info.$image.attr('src', info.path); | 
				
			||||||
 | 
					      } else { | 
				
			||||||
 | 
					        info.loaded = true; | 
				
			||||||
 | 
					        setReadyIfLoaded(); | 
				
			||||||
 | 
					      } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const diffContainerWidth = $container.width() - 300; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function setReadyIfLoaded() { | 
				
			||||||
 | 
					      if (imageInfos[0].loaded && imageInfos[1].loaded) { | 
				
			||||||
 | 
					        initViews(imageInfos[0].$image, imageInfos[1].$image); | 
				
			||||||
 | 
					      } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function initViews($imageAfter, $imageBefore) { | 
				
			||||||
 | 
					      initSideBySide(createContext($imageAfter[0], $imageBefore[0])); | 
				
			||||||
 | 
					      if ($imageAfter.length > 0 && $imageBefore.length > 0) { | 
				
			||||||
 | 
					        initSwipe(createContext($imageAfter[1], $imageBefore[1])); | 
				
			||||||
 | 
					        initOverlay(createContext($imageAfter[2], $imageBefore[2])); | 
				
			||||||
 | 
					      } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      $container.find('> .loader').hide(); | 
				
			||||||
 | 
					      $container.find('> .hide').removeClass('hide'); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function initSideBySide(sizes) { | 
				
			||||||
 | 
					      let factor = 1; | 
				
			||||||
 | 
					      if (sizes.max.width > (diffContainerWidth - 24) / 2) { | 
				
			||||||
 | 
					        factor = (diffContainerWidth - 24) / 2 / sizes.max.width; | 
				
			||||||
 | 
					      } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sizes.image1.css({ | 
				
			||||||
 | 
					        width: sizes.size1.width * factor, | 
				
			||||||
 | 
					        height: sizes.size1.height * factor | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      sizes.image1.parent().css({ | 
				
			||||||
 | 
					        margin: `${sizes.ratio[1] * factor + 15}px ${sizes.ratio[0] * factor}px ${sizes.ratio[1] * factor}px`, | 
				
			||||||
 | 
					        width: sizes.size1.width * factor + 2, | 
				
			||||||
 | 
					        height: sizes.size1.height * factor + 2 | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      sizes.image2.css({ | 
				
			||||||
 | 
					        width: sizes.size2.width * factor, | 
				
			||||||
 | 
					        height: sizes.size2.height * factor | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      sizes.image2.parent().css({ | 
				
			||||||
 | 
					        margin: `${sizes.ratio[3] * factor}px ${sizes.ratio[2] * factor}px`, | 
				
			||||||
 | 
					        width: sizes.size2.width * factor + 2, | 
				
			||||||
 | 
					        height: sizes.size2.height * factor + 2 | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function initSwipe(sizes) { | 
				
			||||||
 | 
					      let factor = 1; | 
				
			||||||
 | 
					      if (sizes.max.width > diffContainerWidth - 12) { | 
				
			||||||
 | 
					        factor = (diffContainerWidth - 12) / sizes.max.width; | 
				
			||||||
 | 
					      } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sizes.image1.css({ | 
				
			||||||
 | 
					        width: sizes.size1.width * factor, | 
				
			||||||
 | 
					        height: sizes.size1.height * factor | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      sizes.image1.parent().css({ | 
				
			||||||
 | 
					        margin: `0px ${sizes.ratio[0] * factor}px`, | 
				
			||||||
 | 
					        width: sizes.size1.width * factor + 2, | 
				
			||||||
 | 
					        height: sizes.size1.height * factor + 2 | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      sizes.image1.parent().parent().css({ | 
				
			||||||
 | 
					        padding: `${sizes.ratio[1] * factor}px 0 0 0`, | 
				
			||||||
 | 
					        width: sizes.max.width * factor + 2 | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      sizes.image2.css({ | 
				
			||||||
 | 
					        width: sizes.size2.width * factor, | 
				
			||||||
 | 
					        height: sizes.size2.height * factor | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      sizes.image2.parent().css({ | 
				
			||||||
 | 
					        margin: `${sizes.ratio[3] * factor}px ${sizes.ratio[2] * factor}px`, | 
				
			||||||
 | 
					        width: sizes.size2.width * factor + 2, | 
				
			||||||
 | 
					        height: sizes.size2.height * factor + 2 | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      sizes.image2.parent().parent().css({ | 
				
			||||||
 | 
					        width: sizes.max.width * factor + 2, | 
				
			||||||
 | 
					        height: sizes.max.height * factor + 2 | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      $container.find('.diff-swipe').css({ | 
				
			||||||
 | 
					        width: sizes.max.width * factor + 2, | 
				
			||||||
 | 
					        height: sizes.max.height * factor + 4 | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      $container.find('.swipe-bar').on('mousedown', function(e) { | 
				
			||||||
 | 
					        e.preventDefault(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const $swipeBar = $(this); | 
				
			||||||
 | 
					        const $swipeFrame = $swipeBar.parent(); | 
				
			||||||
 | 
					        const width = $swipeFrame.width() - $swipeBar.width() - 2; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $(document).on('mousemove.diff-swipe', (e2) => { | 
				
			||||||
 | 
					          e2.preventDefault(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          const value = Math.max(0, Math.min(e2.clientX - $swipeFrame.offset().left, width)); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          $swipeBar.css({ | 
				
			||||||
 | 
					            left: value | 
				
			||||||
 | 
					          }); | 
				
			||||||
 | 
					          $container.find('.swipe-container').css({ | 
				
			||||||
 | 
					            width: $swipeFrame.width() - value | 
				
			||||||
 | 
					          }); | 
				
			||||||
 | 
					          $(document).on('mouseup.diff-swipe', () => { | 
				
			||||||
 | 
					            $(document).off('.diff-swipe'); | 
				
			||||||
 | 
					          }); | 
				
			||||||
 | 
					        }); | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function initOverlay(sizes) { | 
				
			||||||
 | 
					      let factor = 1; | 
				
			||||||
 | 
					      if (sizes.max.width > diffContainerWidth - 12) { | 
				
			||||||
 | 
					        factor = (diffContainerWidth - 12) / sizes.max.width; | 
				
			||||||
 | 
					      } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sizes.image1.css({ | 
				
			||||||
 | 
					        width: sizes.size1.width * factor, | 
				
			||||||
 | 
					        height: sizes.size1.height * factor | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      sizes.image2.css({ | 
				
			||||||
 | 
					        width: sizes.size2.width * factor, | 
				
			||||||
 | 
					        height: sizes.size2.height * factor | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      sizes.image1.parent().css({ | 
				
			||||||
 | 
					        margin: `${sizes.ratio[1] * factor}px ${sizes.ratio[0] * factor}px`, | 
				
			||||||
 | 
					        width: sizes.size1.width * factor + 2, | 
				
			||||||
 | 
					        height: sizes.size1.height * factor + 2 | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      sizes.image2.parent().css({ | 
				
			||||||
 | 
					        margin: `${sizes.ratio[3] * factor}px ${sizes.ratio[2] * factor}px`, | 
				
			||||||
 | 
					        width: sizes.size2.width * factor + 2, | 
				
			||||||
 | 
					        height: sizes.size2.height * factor + 2 | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      sizes.image2.parent().parent().css({ | 
				
			||||||
 | 
					        width: sizes.max.width * factor + 2, | 
				
			||||||
 | 
					        height: sizes.max.height * factor + 2 | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      $container.find('.onion-skin').css({ | 
				
			||||||
 | 
					        width: sizes.max.width * factor + 2, | 
				
			||||||
 | 
					        height: sizes.max.height * factor + 4 | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const $range = $container.find("input[type='range'"); | 
				
			||||||
 | 
					      const onInput = () => sizes.image1.parent().css({ | 
				
			||||||
 | 
					        opacity: $range.val() / 100 | 
				
			||||||
 | 
					      }); | 
				
			||||||
 | 
					      $range.on('input', onInput); | 
				
			||||||
 | 
					      onInput(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					  }); | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,105 @@ | 
				
			|||||||
 | 
					.image-diff-container { | 
				
			||||||
 | 
					  text-align: center; | 
				
			||||||
 | 
					  padding: 30px 0; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  img { | 
				
			||||||
 | 
					    border: 1px solid var(--color-primary-light-7); | 
				
			||||||
 | 
					    background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAG0lEQVQYlWN4+vTpf3SMDTAMBYXYBLFpHgoKAeiOf0SGE9kbAAAAAElFTkSuQmCC) right bottom var(--color-primary-light-7); | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .before-container { | 
				
			||||||
 | 
					    border: 1px solid var(--color-red); | 
				
			||||||
 | 
					    display: block; | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .after-container { | 
				
			||||||
 | 
					    border: 1px solid var(--color-green); | 
				
			||||||
 | 
					    display: block; | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .diff-side-by-side { | 
				
			||||||
 | 
					    .side { | 
				
			||||||
 | 
					      display: inline-block; | 
				
			||||||
 | 
					      line-height: 0; | 
				
			||||||
 | 
					      vertical-align: top; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .side-header { | 
				
			||||||
 | 
					        font-weight: bold; | 
				
			||||||
 | 
					      } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .diff-swipe { | 
				
			||||||
 | 
					    margin: auto; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .swipe-frame { | 
				
			||||||
 | 
					      position: absolute; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .before-container { | 
				
			||||||
 | 
					        position: absolute; | 
				
			||||||
 | 
					      } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .swipe-container { | 
				
			||||||
 | 
					        position: absolute; | 
				
			||||||
 | 
					        right: 0; | 
				
			||||||
 | 
					        display: block; | 
				
			||||||
 | 
					        border-left: 2px solid var(--color-secondary-dark-8); | 
				
			||||||
 | 
					        height: 100%; | 
				
			||||||
 | 
					        overflow: hidden; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .after-container { | 
				
			||||||
 | 
					          position: absolute; | 
				
			||||||
 | 
					          right: 0; | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					      } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .swipe-bar { | 
				
			||||||
 | 
					        z-index: 100; | 
				
			||||||
 | 
					        position: absolute; | 
				
			||||||
 | 
					        height: 100%; | 
				
			||||||
 | 
					        top: 0; | 
				
			||||||
 | 
					        left: 0; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .handle { | 
				
			||||||
 | 
					          background: var(--color-secondary-dark-8); | 
				
			||||||
 | 
					          left: -5px; | 
				
			||||||
 | 
					          height: 12px; | 
				
			||||||
 | 
					          width: 12px; | 
				
			||||||
 | 
					          position: absolute; | 
				
			||||||
 | 
					          transform: rotate(45deg); | 
				
			||||||
 | 
					          box-sizing: border-box; | 
				
			||||||
 | 
					          display: flex; | 
				
			||||||
 | 
					          justify-content: center; | 
				
			||||||
 | 
					          align-items: center; | 
				
			||||||
 | 
					          cursor: pointer; | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .top-handle { | 
				
			||||||
 | 
					          top: -12px; | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .bottom-handle { | 
				
			||||||
 | 
					          bottom: -14px; | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					      } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .diff-overlay { | 
				
			||||||
 | 
					    margin: 0 auto; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .overlay-frame { | 
				
			||||||
 | 
					      margin: 0 auto; | 
				
			||||||
 | 
					      position: relative; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .before-container, | 
				
			||||||
 | 
					    .after-container { | 
				
			||||||
 | 
					      position: absolute; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    input { | 
				
			||||||
 | 
					      width: 300px; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					} | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue