FEATURE: Scroll-based logo on mobile (#6632)

This commit is contained in:
Joe 2018-11-22 10:21:49 +08:00 committed by Kris
parent d984323e23
commit ee6c0170ce
14 changed files with 245 additions and 166 deletions

View File

@ -12,6 +12,9 @@ function highlight(postNumber) {
$contents.on("animationend", () => $contents.removeClass("highlighted"));
}
// used to determine scroll direction on mobile
let lastScroll, scrollDirection, delta;
export default Ember.Component.extend(AddArchetypeClass, Scrolling, {
userFilters: Ember.computed.alias("topic.userFilters"),
classNameBindings: [
@ -94,6 +97,23 @@ export default Ember.Component.extend(AddArchetypeClass, Scrolling, {
this.appEvents.trigger("header:hide-topic");
}
});
// setup mobile scroll logo
if (this.site.mobileView) {
this.appEvents.on("topic:scrolled", offset =>
this.mobileScrollGaurd(offset)
);
// used to animate header contents on scroll
this.appEvents.on("header:show-topic", () => {
$("header.d-header")
.removeClass("scroll-up")
.addClass("scroll-down");
});
this.appEvents.on("header:hide-topic", () => {
$("header.d-header")
.removeClass("scroll-down")
.addClass("scroll-up");
});
}
},
willDestroyElement() {
@ -109,6 +129,11 @@ export default Ember.Component.extend(AddArchetypeClass, Scrolling, {
// this happens after route exit, stuff could have trickled in
this.appEvents.trigger("header:hide-topic");
this.appEvents.off("post:highlight");
// mobile scroll logo clean up.
if (this.site.mobileView) {
this.appEvents.off("topic:scrolled");
$("header.d-header").removeClass("scroll-down scroll-up");
}
},
@observes("Discourse.hasFocus")
@ -123,9 +148,18 @@ export default Ember.Component.extend(AddArchetypeClass, Scrolling, {
},
showTopicInHeader(topic, offset) {
return offset > this.get("dockAt");
// conditions for showing topic title in the header for mobile
if (
this.site.mobileView &&
scrollDirection !== "up" &&
offset > this.dockAt
) {
return true;
// condition for desktops
} else {
return offset > this.dockAt;
}
},
// The user has scrolled the window, or it is finished rendering and ready for processing.
scrolled() {
if (this.isDestroyed || this.isDestroying || this._state !== "inDOM") {
@ -161,5 +195,23 @@ export default Ember.Component.extend(AddArchetypeClass, Scrolling, {
// Trigger a scrolled event
this.appEvents.trigger("topic:scrolled", offset);
},
// determines scroll direction, triggers header topic info on mobile
// and ensures that the switch happens only once per scroll direction change
mobileScrollGaurd(offset) {
// user hasn't scrolled past topic title.
if (offset < this.dockAt) return;
delta = offset - lastScroll;
// 3px buffer so that the switch doesn't happen with tiny scrolls
if (delta > 3 && scrollDirection !== "down") {
scrollDirection = "down";
this.appEvents.trigger("header:show-topic", this.topic);
} else if (delta < -3 && scrollDirection !== "up") {
scrollDirection = "up";
this.appEvents.trigger("header:hide-topic");
}
lastScroll = offset;
}
});

View File

@ -1,3 +1,4 @@
import { getOwner } from "discourse-common/lib/get-owner";
import {
default as computed,
observes
@ -154,15 +155,14 @@ export default Ember.Component.extend({
const $wrapper = this.$();
if (!$wrapper || $wrapper.length === 0) return;
const offset = window.pageYOffset || $("html").scrollTop();
const progressHeight = this.site.mobileView
? 0
: $("#topic-progress").height();
const maximumOffset = $("#topic-bottom").offset().top + progressHeight;
const windowHeight = $(window).height();
const composerHeight = $("#reply-control").height() || 0;
const isDocked = offset >= maximumOffset - windowHeight + composerHeight;
const bottom = $("body").height() - maximumOffset;
const offset = window.pageYOffset || $("html").scrollTop(),
progressHeight = this.site.mobileView ? 0 : $("#topic-progress").height(),
maximumOffset = $("#topic-bottom").offset().top + progressHeight,
windowHeight = $(window).height(),
bodyHeight = $("body").height(),
composerHeight = $("#reply-control").height() || 0,
isDocked = offset >= maximumOffset - windowHeight + composerHeight,
bottom = bodyHeight - maximumOffset;
if (composerHeight > 0) {
$wrapper.css("bottom", isDocked ? bottom : composerHeight);
@ -178,6 +178,23 @@ export default Ember.Component.extend({
} else {
$wrapper.css("right", "1em");
}
// switch mobile scroll logo at the very bottom of topics
const isIOS = this.capabilities.isIOS,
switchHeight = bodyHeight - offset - windowHeight,
appEvents = getOwner(this).lookup("app-events:main");
if (isIOS && switchHeight < -10) {
// match elastic-scroll behaviour in iOS
setTimeout(function() {
appEvents.trigger("header:hide-topic");
}, 300);
} else if (!isIOS && switchHeight < 5) {
// normal switch for everyone else
setTimeout(function() {
appEvents.trigger("header:hide-topic");
}, 300);
}
},
click(e) {

View File

@ -5,9 +5,9 @@ createWidget("header-contents", {
tagName: "div.contents.clearfix",
template: hbs`
{{attach widget="home-logo" attrs=attrs}}
<div class="panel clearfix">{{yield}}</div>
{{#if attrs.topic}}
{{attach widget="header-topic-info" attrs=attrs}}
{{/if}}
<div class="panel clearfix">{{yield}}</div>
`
});

View File

@ -20,7 +20,7 @@ export default createWidget("header-topic-info", {
if (href) {
heading.push(
h(
"a",
"a.private-message-glyph-wrapper",
{ attributes: { href } },
h("span.private-message-glyph", iconNode("envelope"))
)
@ -47,6 +47,7 @@ export default createWidget("header-topic-info", {
const title = [h("h1", heading)];
const category = topic.get("category");
if (loaded || category) {
if (
category &&
@ -54,12 +55,15 @@ export default createWidget("header-topic-info", {
!this.siteSettings.suppress_uncategorized_badge)
) {
const parentCategory = category.get("parentCategory");
const categories = [];
if (parentCategory) {
title.push(
categories.push(
this.attach("category-link", { category: parentCategory })
);
}
title.push(this.attach("category-link", { category }));
categories.push(this.attach("category-link", { category }));
title.push(h("div.categories-wrapper", categories));
}
let extra = [];

View File

@ -26,15 +26,15 @@ export default createWidget("home-logo", {
const logoUrl = siteSettings.site_logo_url || "";
const title = siteSettings.title;
if (!mobileView && this.attrs.minimized) {
if (this.attrs.minimized) {
const logoSmallUrl = siteSettings.site_logo_small_url || "";
if (logoSmallUrl.length) {
return h("img#site-logo.logo-small", {
key: "logo-small",
attributes: {
src: Discourse.getURL(logoSmallUrl),
width: 33,
height: 33,
width: 36,
height: 36,
alt: title
}
});
@ -49,7 +49,7 @@ export default createWidget("home-logo", {
} else if (logoUrl.length) {
return h("img#site-logo.logo-big", {
key: "logo-big",
attributes: { src: Discourse.getURL(logoUrl), alt: title }
attributes: { src: Discourse.getURL(logoUrl), height: 36, alt: title }
});
} else {
return h("h1#site-text-logo.text-logo", { key: "logo-text" }, title);

View File

@ -1,4 +1,6 @@
.d-header {
display: flex;
align-items: center;
width: 100%;
position: absolute;
top: 0;
@ -6,35 +8,46 @@
background-color: $header_background;
box-shadow: shadow("header");
> .wrap {
width: 100%;
height: 100%;
.contents {
display: flex;
align-items: center;
height: 100%;
}
}
.docked & {
position: fixed;
backface-visibility: hidden; /** do magic for scrolling performance **/
}
.contents {
margin: 8px 0;
}
.title {
float: left;
display: flex;
align-items: center;
height: 100%;
a,
a:visited {
color: $header_primary;
}
}
// the logo height is set in the home-logo widget. This ensures we get a scaled
// width that respects the aspect ratio of the image
#site-logo {
max-height: 2.8571em;
width: auto;
}
.d-icon-home {
font-size: 1.643em;
font-size: $font-up-5;
}
.panel {
float: right;
position: relative;
display: flex;
flex: 0 0 auto;
margin-left: auto;
align-items: center;
}
@ -195,3 +208,88 @@
#search-term::-ms-clear {
display: none;
}
// topic info in the header
.extra-info-wrapper {
display: flex;
align-items: center;
flex: 1 1 0%; // unit on flex-basis is required for IE11
height: 100%;
line-height: $line-height-medium;
padding: 0 1.5em 0 0.5em;
// we need to hide overflow in both to truncate the title in a flexbox
overflow: hidden;
.extra-info {
overflow: hidden;
width: 100%;
-webkit-animation: fadein 0.5s;
animation: fadein 0.5s;
}
.topic-link {
color: $header_primary;
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.topic-statuses {
margin-top: -2px;
float: left;
padding: 0;
i {
color: $header_primary;
}
.d-icon-envelope {
color: $danger;
}
.d-icon-lock {
padding-top: 0.15em;
}
.unpinned {
color: $header_primary;
}
}
h1 {
margin: 0 0 0.25em 0;
font-size: $font-up-3;
width: 100%;
}
.categories-wrapper {
display: inline-flex;
max-width: 100%;
// only truncate the last category name.
> a:last-of-type {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.badge-wrapper {
margin-right: 8px;
}
.badge-wrapper.bullet {
.badge-category-parent-bg,
.badge-category-bg {
min-width: 5px;
}
}
.topic-header-extra {
display: inline-flex;
align-items: center;
max-width: 100%;
flex: 1 0 0%; // unit on flex-basis is required for IE11
.discourse-tags {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.discourse-tag {
display: inline;
}
}
}
// if a topic has both categories and tags, the tag container should shrink
// instead of wrapping to the next line.
.categories-wrapper + .topic-header-extra {
min-width: 0;
}
}

View File

@ -37,19 +37,6 @@
}
}
.topic-header-extra .discourse-tag {
-webkit-animation: fadein 0.7s;
animation: fadein 0.7s;
}
.bullet + .topic-header-extra {
display: block;
}
.box + .topic-header-extra {
display: inline-block;
}
.topic-category {
display: flex;
flex-wrap: wrap;

View File

@ -60,8 +60,6 @@
.title-wrapper {
display: flex;
flex-wrap: wrap;
width: 90%;
align-items: flex-end;
.btn-small {
margin: 0 6px 0 0;
}
@ -151,12 +149,6 @@ a.badge-category {
clear: both;
}
.extra-info-wrapper {
.badge-wrapper {
float: left;
}
}
.has-pending-posts {
padding: 0.5em;
background-color: $highlight-medium;

View File

@ -8,10 +8,6 @@ body.widget-dragging {
cursor: ns-resize;
}
header {
margin-bottom: 15px;
}
// Common classes
.boxed {
height: 100%;

View File

@ -4,16 +4,21 @@
.d-header {
left: 0;
padding-top: 3px;
height: 4.2857em;
.d-icon-home {
padding: 8px;
font-size: $font-up-5;
}
height: 4.2858em;
margin-bottom: 15px;
.site-text-logo {
position: relative;
top: 10px;
#site-text-logo {
font-size: $font-up-3;
margin: 0;
}
.extra-info {
// header title should not be centered if there's no tags / categories
&:not(.two-rows) {
min-height: 2.75em;
}
h1 {
margin: 0 0 0.125em 0;
}
}
}
@ -41,11 +46,3 @@
color: dark-light-choose($primary-high, $secondary-low);
}
}
header {
#site-text-logo {
font-size: $font-up-3;
margin-top: 0.4em;
line-height: $line-height-medium;
}
}

View File

@ -11,14 +11,6 @@ h1 .topic-statuses .topic-status i {
vertical-align: middle;
}
.logo-small {
margin-right: 8px;
width: auto;
max-width: 80px;
height: auto;
max-height: 40px;
}
.topic-body {
padding: 0;
&:first-of-type {
@ -506,64 +498,6 @@ video {
}
}
.extra-info-wrapper {
overflow: hidden;
.badge-wrapper,
i,
.topic-link {
-webkit-animation: fadein 0.7s;
animation: fadein 0.7s;
}
.topic-statuses {
i {
color: $header_primary;
}
.d-icon-envelope {
color: $danger;
}
.d-icon-lock {
padding-top: 0.15em;
}
.unpinned {
color: $header_primary;
}
}
.topic-link {
color: $header_primary;
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.topic-header-extra {
margin: 0 0 0 5px;
}
}
/* default docked header CSS for all topics, including those without categories */
.extra-info {
h1 {
margin: 5px 0 0 0;
font-size: $font-up-3;
line-height: $line-height-large;
width: 100%;
}
.topic-statuses {
margin-top: -2px;
}
}
/* override docked header CSS for topics with categories */
.extra-info.two-rows {
h1 {
line-height: $line-height-medium;
margin: 0;
width: 100%;
}
}
.open > .dropdown-menu {
display: block;
}

View File

@ -149,9 +149,3 @@ blockquote {
#simple-container {
width: 90%;
}
// somehow the image logo assumption inherits margins from earlier in the CSS
// stack we must remove margins for text site titles
h1#site-text-logo {
margin: 0 0 0 10px;
}

View File

@ -2,37 +2,50 @@
// Discourse header
// --------------------------------------------------
#site-text-logo {
font-size: $font-up-3;
}
@include breakpoint(mobile-small) {
#site-text-logo {
font-size: $font-up-2;
}
}
.d-header {
#site-logo {
max-width: 8.8em;
}
height: 3.66em;
// some protection for text-only site titles
.title {
max-width: 50%;
height: 39px;
max-width: 75%;
white-space: nowrap;
overflow: hidden;
padding: 0;
text-overflow: clip;
display: table;
text-overflow: ellipsis;
-webkit-animation: fadein 0.5s;
animation: fadein 0.5s;
a {
display: table-cell;
vertical-align: middle;
display: block;
}
}
#site-text-logo {
margin: 0;
font-size: $font-up-1;
}
.extra-info-wrapper {
.extra-info {
// header title should not be centered if there's no tags / categories
&:not(.two-rows) {
min-height: 2.25em;
}
h1 {
font-size: $font-0;
}
.private-message-glyph-wrapper {
float: left;
}
}
}
button.sign-up-button {
display: none;
}
// styles for mobile scroll logo / topic
.panel {
-webkit-animation: fadein 0.5s;
animation: fadein 0.5s;
}
&.scroll-down .panel {
display: none;
}
}
.d-header-icons {

View File

@ -1,5 +1,4 @@
/* hide the reply border above the time gap notices */
.time-gap + .topic-post article {
border-top: none;
}
@ -319,10 +318,6 @@ iframe {
max-width: 100%;
}
.extra-info {
display: none;
}
.open > .dropdown-menu {
display: block;
}