写点什么

Photoshop Web 版本用了哪些 CSS 技术

作者:南城FE
  • 2023-11-07
    广东
  • 本文字数:6898 字

    阅读完需:约 23 分钟

本文翻译自 CSS Findings From Photoshop Web Version,作者:Ahmad, 略有删改。


几周前,Adobe 发布了一个 Web 版的 Photoshop,它是用 WebAssembly、Web 组件、P3 颜色等网络技术构建的。


Photoshop 是我 14 岁时学会的第一个专业设计应用程序。这是我成为设计师并最终成为前端开发人员的原因之一。正因为如此,我认为看看 CSS 是如何助力像 Photoshop 这样的大型应用开发会很有趣。



在这篇文章中,我将分享在 Web 版的 Photoshop 中我觉得有趣的 CSS 发现。

Photoshop 旧版 Logo

我注意到的第一件事是在浏览器控制台中使用 Photoshop(1990-1991)的旧 Logo。



你会对这样的东西是如何制作的感到好奇吗?以下是代码:


console.info(  "%c %cAdobe %cPhotoshop Web%c  %c2023.22.0.0%c  %c56043548b47",  "padding-left: 36px; line-height: 36px; background-image: url('data:image/gif;base64,R0lGODlhIAAgAPEBAAAAAP///wAAAAAAACH5BAEAAAIALAAAAAAgACAAAAKkhC+py3zfopxGvIsztsCGD4La2FVAGBoBuZnox45WcqLsum5KDWdvf1nwTo+fLbgDqo7LFCJJbOY0oidt6ozVKrtib0qaCnlYcJh7rf5iK6HZaM64VeS6L+pWf89WT+6vRAUBBVQ1gpOTJ4IYdxCnOBSJ8ZhkZNekk5ZSxpTpt6Y1eEVm00j3VALDmBXVyPEJB2r2ShoLh2ASqvU60dsr5yuBUQAAOw=='); background-size: 32px; background-repeat: no-repeat; background-position: 2px 2px", "background: #666; border-radius:0.5em 0 0 0.5em; padding:0.2em 0em 0.1em 0.5em; color: white; font-weight: bold", "background: #666; border-radius:0 0.5em 0.5em 0; padding:0.2em 0.5em 0.1em 0em; color: white;", "", "background: #c3a650; border-radius:0.5em; padding:0.2em 0.5em 0.1em 0.5em; color: white;", "", "background: #15889f; border-radius:0.5em; padding:0.2em 0.5em 0.1em 0.5em; color: white;");
复制代码

body 元素

要让 Photoshop 这样的应用在 Web 网页上有真实应用的感觉,首先需要防止滚动。为了实现这一点,<body>元素设置了position: fixedoverflow: hidden


body,html {  height: 100%;}
body { font-family: adobe-clean, sans-serif; margin: 0; overflow: hidden; position: fixed; width: 100%;}
复制代码


<body>元素内部,也有多个根元素。


<psw-app>  <psw-app-context>    <ue-video-surface>      <ue-drawer>        <div id="appView">          <psw-app-navbar></psw-app-navbar>          <psw-document-page></psw-document-page>        </div>      </ue-drawer>    </ue-video-surface>  </psw-app-context></psw-app>
复制代码


最里面是包含导航和文档页面的元素 #appView


#appView {  background-color: var(--editor-background-color);  color: var(--spectrum-global-color-gray-800);  display: flex;  flex-direction: column;}
复制代码


几乎都是 Flexbox 布局

当构建一个 web 应用程序时,使用flexbox有很多好处。当我想到Flexbox和 Photoshop 一起出现时,我的感觉是很复杂。



Photoshop 是一个著名的设计软件,是许多人进入设计领域的第一款软件。另一方面使用 Flexbox 构建组件变得更容易,对 CSS 对新手来说更容易。


无需使用clearfix清除浮动,只需添加display: flex,然后根据需要设置子项的样式。让我们探索 Photoshop 中的相关Flexbox使用情况。

导航栏

我喜欢这里的命名。他们使用“start”,“center”和“end”,而不是使用“leftcenterright”,。



对于可以从左到右(LTR)或从右到左(RTL)工作的应用程序来说,这种逻辑命名是正确的。

Context Bar

在构建像 Photoshop 这样的复杂应用程序时,嵌套的flexbox容器是必要的。在下图中,我在上下文栏中突出显示了两个容器。



第一个容器用于抓取动作。第二个容器包含所有操作和按钮。


.container {  display: flex;  flex-wrap: nowrap;  align-items: center;  gap: var(--spectrum-global-dimension-size-50);}
复制代码


  • gap的使用对定义间距有很大帮助。相比使用 margin 或 padding 就好多了。

  • 名称.container太通用了,但它在这里恰到好处,因为这是一个 Web 组件,所有的样式都被封装在内部。

图层

由于图层功能是 Photoshop 的重要组成部分,因此它可能是新手将要学习的前几件事之一。我好奇地检查了它们背后的 CSS 实现。


这里是层组件的 HTML 代码:


<psw-tree-view-item indent="0" layer-visible can-open dir="ltr" open>  <div id="link">    <span id="first-column"></span>    <span id="second-column"></span>    <span id="label"></span>  </div></psw-tree-view-item>
复制代码


你认为这里使用 ID 是完全可以的吗?由于这是一个 Web 组件,所以#first-column ID在页面上出现多少次并不重要。



#link元素是主要的flexbox包装器,#label中的元素也是flexbox包装器。


<div class="layer-content layer-wrapper selected">  <psw-layer-thumbnail></psw-layer-thumbnail>  <div class="name" title="Layer name">Layer name</div>  <div class="actions"></div>  <overlay-trigger></overlay-trigger></div>
复制代码


让我们看看子层的缩进是如何完成的。



  • :host()表示层组件

  • 如果有 HTML 属性存在indent=1,则更改第一列的padding-right


CSS :host 是一个伪类选择器,它用于选择当前组件的宿主元素。:host 选择器只能在 Shadow DOM 中使用,因为它选择的是组件的根元素,而不是组件内部的子元素。


:host([dir="ltr"][indent="1"]) #first-column {  padding-right: var(--spectrum-global-dimension-size-200);}
复制代码


如果是indent=2,则通过 CSS calc()函数将padding-right的值乘以 2。


:host([dir="ltr"][indent="2"]) #first-column {  padding-right: calc(2 * var(--spectrum-global-dimension-size-200));}
复制代码



在浏览器中,我尝试嵌套到第 6 级。下面是一张真实的截图:



当看到这个的时候,我检查Figma背后的 CSS 实现。他们使用了一个间隔组件来增加嵌套层的间距。



有趣的是,两个主要的设计应用程序使用了不同的技术来实现相同的目标。

关于 CSS Grid 布局

新建文件弹窗

创建新的 Photoshop 文件时,您可以选择预定义的大小列表。为了实现这一点,有一个包含多个选项卡和一个活动面板的布局。



HTML 代码如下:


<sp-tabs  id="tabs"  quiet=""  selected="2"  size="m"  direction="horizontal"  dir="ltr"  focusable="">  <div id="list"></div>  <slot name="tab-panel"></slot></sp-tabs>
复制代码


在 CSS 中,有一个 1 列 2 行的主网格。第一行是 auto,第二行跨越可用空间。


:host {  display: grid;  grid-template-columns: 100%;}
:host(:not([direction^="vertical"])) { grid-template-rows: auto 1fr;}
复制代码


这里有几件事:


  • 使用 CSS:not()选择器

  • 使用[attr^=value]选择器排除属性direction的值以vertical开头的 HTML 元素。


我认为这是一种条件 CSS 技术。


我尝试将 direction 属性更改为 vertical。



下面是基于属性更改的 CSS:


:host([direction^="vertical"]) {  grid-template-columns: auto 1fr;}
:host([direction^="vertical-right"]) #list #selection-indicator,:host([direction^="vertical"]) #list #selection-indicator { inline-size: var( --mod-tabs-divider-size, var(--spectrum-tabs-divider-size) ); inset-block-start: 0px; inset-inline-start: 0px; position: absolute;}
复制代码


要突出显示哪个选项卡项处于活动状态,有一个相对于选项卡列表定位的#selection-indicator元素。

图层属性

我很喜欢这里的 CSS 网格。它适用于在网格中对齐多个元素的问题。



在 CSS 中,我注意到以下代码:


.content {  position: relative;  display: grid;  grid-template-rows: [horizontal] min-content [vertical] min-content [transforms] min-content [end];  grid-template-columns: [size-labels] min-content [size-inputs] auto [size-locks] min-content [space] min-content [position-labels] min-content [position-inputs] auto [end];  row-gap: var(--spectrum-global-dimension-size-150);}
复制代码


这里使用的技术称为命名网格线。这个想法是你命名每个列或网格,然后定义其宽度。列和行的宽度为automin-content。这是制作动态网格的好方法。



这样每个网格项都应该定位在网格中。以下是一些例子:


.horizontal-size-label {  grid-area: horizontal / size-labels / horizontal / size-labels;}
.vertical-position-input { grid-area: vertical / position-inputs / vertical / position-inputs;}
.horizontal-position-input { grid-area: horizontal / position-inputs / horizontal / position-inputs;}
复制代码


另一个引起我注意的细节是在网格项中使用position: absolute。锁定按钮被放置在网格的中心,但它需要在lefttop位置稍微偏移一些。


.lock-button {  grid-area: horizontal / size-locks / horizontal / size-locks;  position: absolute;  left: 8px;  top: 22px;}
复制代码

Drop-Shadow 输入框

这是许多 CSS 网格用于输入字段布局的示例。


:host([editable]) {  display: grid;  grid-template-areas:    "label ."    "slider number";  grid-template-columns: 1fr auto;}
:host([editable]) #label-container { grid-area: label / label / label / label;}
:host([editable]) #label-container + div { grid-area: slider / slider / slider / slider;}
:host([editable]) sp-number-field { grid-area: number / number / number / number;}
复制代码


在浏览器中检查时,可以看到轴网线名称或轴网区域名称。



对应网格线名称:



你可以用两种不同的方式查看布局,对于调试或理解您试图构建/修复的布局非常有用。CSS 网格应该在我们的 Web 应用程序中更多地使用,但绝对不像下面的例子。

菜单网格


我认为在这里使用 CSS 网格布局有点过头了,下面说明一下我的理解。


sp-menu-item {  display: grid;  grid-template-areas:    ". chevronAreaCollapsible . iconArea sectionHeadingArea . . ."    "selectedArea chevronAreaCollapsible checkmarkArea iconArea labelArea valueArea actionsArea chevronAreaDrillIn"    ". . . . descriptionArea . . ."    ". . . . submenuArea . . .";  grid-template-columns: auto auto auto auto 1fr auto auto auto;  grid-template-rows: 1fr auto auto auto;}
复制代码


这是一个包含 8 列 * 4 行的网格。从我花费的时间来理解他们为什么这样做,似乎一次只有一行网格是活跃的,其他行会因为内容为空或者缺少 HTML 元素而折叠。


有趣的是,上面的 CSS 是我简化后的。原始版本看起来像这样,团队使用了grid-template速记。



以下是我可以在应用程序中找到的相关菜单项。



这个 CSS 网格是为了这个小组件而设计的,我认为在这里使用 CSS 网格是一种过度设计。


下面是一个使用网格的例子。


.checkmark {  align-self: start;  grid-area: checkmarkArea / checkmarkArea / checkmarkArea /    checkmarkArea;}
#label { grid-area: labelArea / labelArea / labelArea / labelArea;}
::slotted([slot="value"]) { grid-area: valueArea / valueArea / valueArea / valueArea;}
复制代码



请注意 CSS 网格中的灰色部分是不活动的。它们因为没有内容而被折叠了。对于这个具体的例子,作者也可以这样做:


.checkmark {  align-self: start;  grid-area: checkmarkArea;}
#label { grid-area: labelArea;}
::slotted([slot="value"]) { grid-area: valueArea;}
复制代码


当它们是相同的值时,不需要定义每个列和行的开始和结束。

大量使用 CSS 变量

我真的很喜欢 CSS 变量如何用来改变 UI。我将着重指出这方面的多个例子。

更改图层缩略图的大小

如果您熟悉 Photoshop,则可以控制缩略图大小并使其更小。当您有很多层,并希望在更少的空间中查看更多层时,这很有用。



首先层面板的主容器上有一个 HTML 属性large-thumbs


<psw-layers-panel large-thumbs></psw-layers-panel>
复制代码


在 CSS 中,有:host([large-thumbs])分配特定的 CSS 变量。


:host([large-thumbs]) {  --psw-custom-layer-thumbnail-size: var(    --spectrum-global-dimension-size-800  );  --psw-custom-layer-thumbnail-border-size: var(    --spectrum-global-dimension-size-50  );}
复制代码


对于每个层,都有一个名为psw-layer-thumbnail的元素。这是 CSS 变量将被应用的地方。它将从主容器继承它。


<psw-layers-panel-item>  <psw-tree-view-item>    <psw-layer-thumbnail class="thumb"></psw-layer-thumbnail>  </psw-tree-view-item></psw-layers-panel-item>
复制代码


这里 CSS 变量被分配给缩略图。


:host {  --layer-thumbnail-size: var(    --psw-custom-layer-thumbnail-size,    var(--spectrum-global-dimension-size-400)  );  --layer-badge-size: var(--spectrum-global-dimension-size-200);  position: relative;  width: var(--layer-thumbnail-size);  min-width: var(--layer-thumbnail-size);  height: var(--layer-thumbnail-size);}
复制代码

Loading 进度条

管理组件的大小是通过使用属性size来完成的,CSS 变量根据大小而变化。



:host([size="m"]) {  --spectrum-progressbar-size-default: var(    --spectrum-progressbar-size-2400  );  --spectrum-progressbar-font-size: var(--spectrum-font-size-75);  --spectrum-progressbar-thickness: var(    --spectrum-progress-bar-thickness-large  );  --spectrum-progressbar-spacing-top-to-text: var(    --spectrum-component-top-to-text-75  );}
复制代码

图像控件

如果 HTML 属性quite存在,则 UI 更简单。



这也可以通过 CSS 变量来实现。


:host([quiet]) {  --spectrum-actionbutton-background-color-default: var(    --system-spectrum-actionbutton-quiet-background-color-default  );  --spectrum-actionbutton-background-color-hover: var(    --system-spectrum-actionbutton-quiet-background-color-hover  );  /* And a lot more styles that I removed for the purpose of keeping the article clean. */}
复制代码

单选按钮

在这个例子中,团队使用 CSS 变量根据size HTML 属性更改单选按钮的大小。



<sp-radio size="m" checked="" role="radio"></sp-radio>
复制代码


:host([size="m"]) {  --spectrum-radio-height: var(--spectrum-component-height-100);  --spectrum-radio-button-control-size: var(    --spectrum-radio-button-control-size-medium  );  /* And a lot more styles that I removed for the purpose of keeping the article clean. */}
复制代码

当菜单处于活动状态时锁定页面

当主菜单处于活动状态时,有一个“保持器”元素填充整个屏幕,位于菜单下方。



#actual[aria-hidden] + #holder {  display: flex;}
#holder { display: none; align-items: center; justify-content: center; flex-flow: column; width: 100%; height: 100%; position: absolute; top: 0; left: 0;}
复制代码


此元素用于防止用户点击或悬停在页面的其他部分,看起来像在模仿桌面应用程序。

混合模式菜单

我在这里发现了 CSS viewport单元的用途。混合模式菜单的最大高度为55vh



sp-menu {  max-height: 55vh;  --mod-menu-item-min-height: auto;}
::slotted(*) { overscroll-behavior: contain;}
复制代码


overscroll-behavior: contain也有用到。这是一个很好的功能,可以避免滚动正文内容。

注释组件

用户可以在画布上的任何地方钉上注释或绘图。我检查了组件,以了解它是如何构建的。

我喜欢动态定位和颜色的 CSS 变量


为了将每个评论放置在用户选择的位置,团队使用了通过 JS 提供的 CSS 变量来处理。


<div  data-html2canvas-ignore="true"  class="Pin__component ccx-annotation"  style="    --offset-x: 570.359375px;    --offset-y: 74.23046875px;    --ccx-comments-pin-color: #16878C;  "></div>
复制代码


.Pin__component {  --pin-diameter: 24px;  left: calc(var(--offset-x) - var(--pin-diameter) / 2);  top: calc(var(--offset-y) - var(--pin-diameter) / 2);  position: absolute;  height: var(--pin-diameter);  width: var(--pin-diameter);  border-radius: var(--pin-diameter);  border: 1px solid white;  background: var(--ccx-comments-pin-color);}
复制代码

使用 SVG 进行工程图标注

当你将图片缩小时,SVG 笔划不会调整大小,而且看起来很粗。



据我所知,这可以通过添加vector-effect: non-scaling-stroke来解决。但我没试过。

对图层缩略图使用 Object-Fit: Contain

在图层面板中,缩略图使用object-fit: contain以避免失真。



最后

文章到此就结束了,介绍了 Photoshop Web 版本使用的一些 CSS 技术。与国内常见的 CSS 技术相比,有许多不同之处,其中很多部分值得学习和借鉴。当然这只是其中的一部分,如果你感兴趣,可以查看他们的源代码来深入研究。




看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~


专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)

发布于: 刚刚阅读数: 6
用户头像

南城FE

关注

还未添加个人签名 2019-02-12 加入

专注前端开发,分享前端知识

评论

发布
暂无评论
Photoshop Web版本用了哪些CSS技术_CSS_南城FE_InfoQ写作社区